Skip to content

[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
mainfrom
issue/chunk_4_service_account
Open

[backend] feat(service-account): use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973)#5960
EvaE-Filigran wants to merge 63 commits into
mainfrom
issue/chunk_4_service_account

Conversation

@EvaE-Filigran
Copy link
Copy Markdown
Contributor

@EvaE-Filigran EvaE-Filigran commented May 28, 2026

Proposed changes

  • Replace the hardcoded admin token in implant commands with a per-tenant service account token resolved at execution time via ServiceAccountPrivilegeService
  • Unify the token placeholder from $token to #{token} across all executor context services, aligning it with the existing #{inject}, #{agent}, #{tenant} pattern
  • Remove the deprecated replaceArgs (implemented in the chunk_3) overload (6-arg version with token) in ExecutorHelper and integrate token replacement directly into the single replaceArgs method
  • Pass the tenant service account token through the full executor chain: ExecutionExecutorServiceExecutorContextService → each concrete implementation (Caldera, CrowdStrike, PaloAlto Cortex, SentinelOne, Tanium, OpenAEV)

Testing Instructions

  1. Ensure a tenant with a provisioned service account exists (created via datapack V20260518_Service_Account / ServiceAccountPrivilegeService.ensurePrivilegedUserExists)
  2. Trigger an inject execution on any executor (Caldera, CrowdStrike, SentinelOne, Tanium, PaloAlto Cortex)
  3. Verify the execution

Related issues

Checklist

  • I consider the submitted work as finished
  • I tested the code for its functionality
  • I wrote test cases for the relevant uses case
  • I added/update the relevant documentation (either on github or on notion)
  • Where necessary I refactored code to improve the overall quality
  • For bug fix -> I implemented a test that covers the bug

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.

EvaE-Filigran and others added 30 commits May 20, 2026 09:55
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>
…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
…, 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.
Copilot AI review requested due to automatic review settings May 28, 2026 06:55
@EvaE-Filigran EvaE-Filigran self-assigned this May 28, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ExecutionExecutorServiceExecutorContextService implementations and update placeholder replacement to support #{token}.
  • Backend: extend ServiceAccountPrivilegeService with 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): getOpenAevAgentInstaller is 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 thread openaev-api/src/main/java/io/openaev/rest/executor/ExecutorApi.java
Comment thread openaev-api/src/main/java/io/openaev/service/EndpointService.java
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"));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NA

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`;
};
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NA

@EvaE-Filigran
Copy link
Copy Markdown
Contributor Author

Tests ok for :

  • tanium (windows)
  • oaev (macOs)
  • crowdstrike (linux)

# 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
@EvaE-Filigran EvaE-Filigran changed the title Issue/chunk 4 service account [backend] Use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973) May 29, 2026
@EvaE-Filigran EvaE-Filigran changed the title [backend] Use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973) [backend] fet(service-account): Use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973) May 29, 2026
@EvaE-Filigran EvaE-Filigran changed the title [backend] fet(service-account): Use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973) [backend] feat(service-account): Use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973) May 29, 2026
@EvaE-Filigran EvaE-Filigran changed the title [backend] feat(service-account): Use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973) [backend] feat(service-account): use user service account token for executor: Tanium, PaloAlto, SentinelOne, Crowdstrike and Caldera (#5973) May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Service Accounts] US.2-T.2 — Auto-provision service account on tenant creation with correct group/role/capabilities & use it on all executors

2 participants