[backend] feat(service-account): use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973)#5960
Open
EvaE-Filigran wants to merge 63 commits into
Conversation
… service account.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…ycle of the agent and implant, and clean up the previous usage.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… user service account.
…nto issue/chunk_2_service_account
…tion on existing role.
…delete on group, role and user. Using reserved name: email regex (user) and name (group and role).
# Conflicts: # openaev-api/src/main/java/io/openaev/opencti/connectors/service/PrivilegeService.java # openaev-api/src/main/java/io/openaev/service/RoleService.java
…roup id and tenantId
…unk_3_service_account
…, following the usage of id fix for each role and group by tenant.
…ce_account # Conflicts: # openaev-api/src/main/java/io/openaev/service/RoleService.java
… this case. Datapack should be able to update role.
…he player to delete does not exist, replace ReservedName with ReservedKey by id (group and role tests)
…ce_account # Conflicts: # openaev-api/src/test/java/io/openaev/rest/role/TenantRoleReservedNameApiTest.java
…dNameApiTest to TenantGroupReservedKeyApiTest
…_account # Conflicts: # openaev-api/src/main/java/io/openaev/service/account/ServiceAccountPrivilegeService.java # openaev-api/src/test/java/io/openaev/datapack/packs/V20260518_Service_AccountTest.java # openaev-api/src/test/java/io/openaev/service/account/ServiceAccountPrivilegeServiceTest.java
…of token, fix test register EndpointServiceIntegrationTest, add 1 more test with user admin.
…xecutor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera. Clean code, fix tests.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR advances the multi-tenant service-account rollout by removing reliance on a platform-wide admin token and instead resolving a per-tenant service-account token for executor/implant workflows. It also standardizes executor placeholder substitution to use #{token} consistently through the executor chain.
Changes:
- Backend: propagate a tenant service-account token through
ExecutionExecutorService→ExecutorContextServiceimplementations and update placeholder replacement to support#{token}. - Backend: extend
ServiceAccountPrivilegeServicewith helpers to fetch the service-account user and token for a tenant; update agent installer generation to use the service-account token. - Frontend: simplify agent installation URL building by removing user token retrieval/embedding.
Reviewed changes
Copilot reviewed 28 out of 29 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| openaev-front/src/admin/components/agents/InstructionSelector.tsx | Removes token-in-URL installer construction; builds installer URL by installation mode only. |
| openaev-front/src/admin/components/agents/Agents.tsx | Stops fetching/measuring user tokens for the agent installation dialog flow. |
| openaev-front/src/actions/helper.d.ts | Removes MeTokensHelper typing from the helper interface types. |
| openaev-api/src/test/java/io/openaev/service/endpoint/EndpointServiceIntegrationTest.java | Ensures service-account bootstrap occurs in tests before token-dependent upgrade command generation. |
| openaev-api/src/test/java/io/openaev/service/account/ServiceAccountPrivilegeServiceTest.java | Adds unit tests covering service-account token retrieval and error cases. |
| openaev-api/src/test/java/io/openaev/service/account/AgentRuntimeAccessControlTest.java | Adds an admin-allowed registration test case. |
| openaev-api/src/test/java/io/openaev/integration/impl/OpenAEVExecutorIntegrationTest.java | Updates integration factory wiring to include ServiceAccountPrivilegeService. |
| openaev-api/src/test/java/io/openaev/executors/tanium/service/TaniumExecutorServiceTest.java | Updates context service calls to include the new token parameter. |
| openaev-api/src/test/java/io/openaev/executors/sentinelone/service/SentinelOneExecutorServiceTest.java | Updates context service calls to include the new token parameter. |
| openaev-api/src/test/java/io/openaev/executors/ExecutorHelperTest.java | Adds coverage for ExecutorHelper.replaceArgs including #{token} replacement. |
| openaev-api/src/test/java/io/openaev/executors/execution/service/ExecutionExecutorServiceTest.java | Mocks service-account token resolution and validates new token-plumbed calls. |
| openaev-api/src/test/java/io/openaev/executors/crowdstrike/service/CrowdstrikeExecutorServiceTest.java | Updates context service calls to include the new token parameter. |
| openaev-api/src/test/java/io/openaev/executors/caldera/service/CalderaExecutorServiceTest.java | Updates single-agent execution calls to include the new token parameter. |
| openaev-api/src/test/java/io/openaev/datapack/packs/V20260518_Service_AccountTest.java | Adjusts mocking approach for datapack processing to use a local Mockito mock. |
| openaev-api/src/main/java/io/openaev/service/EndpointService.java | Removes admin token config injection; uses service-account token when generating upgrade commands. |
| openaev-api/src/main/java/io/openaev/service/account/ServiceAccountPrivilegeService.java | Adds tenant service-account lookup and token retrieval API. |
| openaev-api/src/main/java/io/openaev/rest/executor/ExecutorApi.java | Changes installer endpoint path (removes token path variable) and resolves service-account token internally. |
| openaev-api/src/main/java/io/openaev/integration/impl/injectors/openaev/OpenaevImplantCommandBuilder.java | Switches implant command token variable to #{token} placeholder. |
| openaev-api/src/main/java/io/openaev/integration/impl/executors/openaev/OpenAEVExecutorIntegrationFactory.java | Injects and forwards ServiceAccountPrivilegeService into OpenAEV executor integration. |
| openaev-api/src/main/java/io/openaev/integration/impl/executors/openaev/OpenAEVExecutorIntegration.java | Passes ServiceAccountPrivilegeService into the constructed context service. |
| openaev-api/src/main/java/io/openaev/executors/tanium/service/TaniumExecutorContextService.java | Passes token into command replacement and executor action construction. |
| openaev-api/src/main/java/io/openaev/executors/sentinelone/service/SentinelOneExecutorContextService.java | Passes token into command replacement and unix command construction. |
| openaev-api/src/main/java/io/openaev/executors/paloaltocortex/service/PaloAltoCortexExecutorContextService.java | Passes token into command replacement for Windows/Unix actions. |
| openaev-api/src/main/java/io/openaev/executors/openaev/service/OpenAEVExecutorContextService.java | Adds token parameter to method signatures and uses replaceArgs(..., token). |
| openaev-api/src/main/java/io/openaev/executors/ExecutorHelper.java | Unifies placeholder replacement, adding #{token} substitution to the core method. |
| openaev-api/src/main/java/io/openaev/executors/ExecutorContextService.java | Extends the executor context service contract to include a token argument. |
| openaev-api/src/main/java/io/openaev/executors/crowdstrike/service/CrowdStrikeExecutorContextService.java | Passes token into all command-replacement paths. |
| openaev-api/src/main/java/io/openaev/executors/caldera/service/CalderaExecutorContextService.java | Adds token trait into Caldera exploit call; updates method signatures. |
| openaev-api/src/main/java/io/openaev/execution/ExecutionExecutorService.java | Resolves tenant service-account token once per executor dispatch and forwards it downstream. |
Comments suppressed due to low confidence (1)
openaev-api/src/main/java/io/openaev/rest/executor/ExecutorApi.java:404
- issue (blocking):
getOpenAevAgentInstalleris still marked@AccessControl(skipRBAC = true)but the URL no longer carries any secret token. Because the handler now resolves the tenant service-account token server-side and returns an install command containing that token, an unauthenticated caller can fetch a valid service-account token (e.g. via/api/tenants/{tenantId}/agent/installer/...). Reintroduce a secret (e.g. keep a token path/query param) or require authenticated access for these installer endpoints, and avoid returning the token to unauthenticated clients.
// Public API
@Operation(
summary = "Retrieve OpenAEV Agent Installer Command",
description =
"Generates the installation command for the OpenAEV agent for the specified platform, installation mode and token.")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Successfully generated the install command."),
@ApiResponse(responseCode = "400", description = "Invalid platform specified."),
@ApiResponse(responseCode = "404", description = "Token not found."),
})
@GetMapping(
value = {
AGENT_URI + "/installer/openaev/{platform}/{installationMode}",
TENANT_AGENT_URI + "/installer/openaev/{platform}/{installationMode}"
})
@AccessControl(skipRBAC = true)
public @ResponseBody ResponseEntity<String> getOpenAevAgentInstaller(
@Parameter(
description =
"Target platform for the agent installation (e.g., windows, linux, mac). Case insensitive.",
required = true)
@PathVariable
String platform,
@Parameter(
description = "Installation Mode: session, user or system service",
required = true)
@PathVariable
String installationMode,
@Parameter(description = "Installation directory") @RequestParam(required = false)
String installationDir,
@Parameter(description = "Service name") @RequestParam(required = false) String serviceName)
throws IOException {
String resolvedPlatform =
AgentUtils.normaliseSupportedAgentPlatform(platform).name().toLowerCase();
String resolvedInstallationMode = AgentUtils.getSupportedInstallationMode(installationMode);
// FIND TOKEN BY TENANT
String token =
privilegeService.getTokenUserServiceAccountByTenant(TenantContext.getCurrentTenant());
String installCommand =
this.endpointService.generateInstallCommand(
resolvedPlatform,
token,
resolvedInstallationMode,
installationDir,
serviceName,
TenantContext.getCurrentTenant());
return ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN).body(installCommand);
Comment on lines
+111
to
+118
| @Transactional(readOnly = true) | ||
| public String getTokenUserServiceAccountByTenant(String tenantId) { | ||
|
|
||
| return getUserServiceAccountByTenant(tenantId) | ||
| .map(User::getTokens) | ||
| .filter(tokens -> !tokens.isEmpty()) | ||
| .map(tokens -> tokens.getFirst().getValue()) | ||
| .orElseThrow(() -> new UnsupportedOperationException("Token not found")); |
Comment on lines
62
to
66
| const buildInstallationUrl = (baseUrl: string) => { | ||
| if (currentTab === 'Standard Installation') return `${baseUrl}/session-user/${userToken?.token_value}`; | ||
| if (currentTab === 'Advanced Installation' && selectedOption === USER) return `${baseUrl}/service-user/${userToken?.token_value}`; | ||
| return `${baseUrl}/service/${userToken?.token_value}`; | ||
| if (currentTab === 'Standard Installation') return `${baseUrl}/session-user`; | ||
| if (currentTab === 'Advanced Installation' && selectedOption === USER) return `${baseUrl}/service-user`; | ||
| return `${baseUrl}/service`; | ||
| }; |
4 tasks
Contributor
Author
|
Tests ok for :
|
# Conflicts: # openaev-api/src/main/java/io/openaev/integration/impl/injectors/openaev/OpenaevImplantCommandBuilder.java # openaev-api/src/test/java/io/openaev/executors/ExecutorHelperTest.java # openaev-api/src/test/java/io/openaev/service/account/ServiceAccountPrivilegeServiceTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed changes
ServiceAccountPrivilegeService$tokento#{token}across all executor context services, aligning it with the existing#{inject},#{agent},#{tenant}patternreplaceArgs(implemented in the chunk_3) overload (6-arg version with token) inExecutorHelperand integrate token replacement directly into the singlereplaceArgsmethodExecutionExecutorService→ExecutorContextService→ each concrete implementation (Caldera, CrowdStrike, PaloAlto Cortex, SentinelOne, Tanium, OpenAEV)Testing Instructions
ServiceAccountPrivilegeService.ensurePrivilegedUserExists)Related issues
Checklist
Further comments
Why this change:
Previously, the implant command builder used the platform-wide admin token (
cfg.getAdminToken()). This was a security concern in a multi-tenant context, all tenants' agents would authenticate with the same admin token.