Skip to content

[Contextual Security][GCP auditlogs] add type-specific entity fields to gcp-auditlogs data stream - #15930

Merged
alexreal1314 merged 6 commits into
elastic:mainfrom
alexreal1314:14512-gcp-auditlogs-esc-schema
Dec 7, 2025
Merged

[Contextual Security][GCP auditlogs] add type-specific entity fields to gcp-auditlogs data stream#15930
alexreal1314 merged 6 commits into
elastic:mainfrom
alexreal1314:14512-gcp-auditlogs-esc-schema

Conversation

@alexreal1314

@alexreal1314 alexreal1314 commented Nov 11, 2025

Copy link
Copy Markdown
Contributor

Proposed commit message

This PR enhances entity classification by populating type-specific fields - user.entity.id, service.entity.id, host.entity.id, entity.id for actor fields alongside existing legacy fields.
Same is relevant for target fields - user.target.entity.id, service.target.entity.id, host.target.entity.id, entity.target.id.

The implementation uses ARN parsing and resource ID pattern matching to classify AWS entities into appropriate categories, enabling better entity visualization in Security Solution while maintaining backward compatibility.

WHAT:

Ingest Pipeline Updates (default.yml)

  • Pattern: Intelligent entity classification using conditional logic based on AWS resource patterns
  • Algorithm: Multi-tier classification strategy:
    • Check userIdentity.type for actor classification
    • Parse patterns (:serviceAccount:, user:, /instances)
    • Apply string operations (contains(), startsWith()) for pattern matching

Implementation:

  • Dual field population: Legacy fields + new type-specific fields
  • TreeSet-based categorization for deterministic ordering
  • Backward-compatible field assignment ensures no breaking changes

WHY:

Primary Goals

  1. ECS Schema Alignment: Moving toward the standardized ECS entity schema pattern for cloud integrations
  2. Better Entity Classification: Generic entity IDs don't provide context about what type of resource is involved in an action
  3. Enhanced Graph Visualization: The Security Solution's graph feature requires entity type information to properly classify and visualize relationships between actors and targets.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices

Author's Checklist

  • [ ]

How to test this PR locally

  1. Clone the following branch.
  2. Set env variables to point to your local env or a remote env - installation guide.
  3. Open gcp-auditlogs integration, make sure the version is v2.46.0.
  4. install integration.
  5. Open discovery and choose data-view logs-* and apply filter data_stream.dataset: gcp.audit
  6. Document should contain one of user/host/service.entity.id fields and one of user/host/service/target.entity.

Related issues

Screenshots

GCP auditlogs:
image

Every document with actor.entity.id field is mapped to at least one of user/host/service/''.entity.id:

query:

FROM logs-*
| WHERE data_stream.dataset == "gcp.audit"
| EVAL actor_user_id = user.entity.id
| EVAL actor_host_id = host.entity.id
| EVAL actor_service_id = service.entity.id
| EVAL actor_entity_id = entity.id
| EVAL actor_backward_id = actor.entity.id
| WHERE actor_user_id IS NULL AND actor_host_id IS NULL AND actor_service_id IS NULL AND actor_entity_id IS NULL AND actor_backward_id IS NOT NULL
| STATS docs_with_any_missing = COUNT(*)

image

Every document with target.entity.id field is mapped to at least one of user/host/service.target.entity.id or entity.target.id:

query:

FROM logs-*
| WHERE data_stream.dataset == "gcp.audit"
| EVAL actor_user_target_id = user.target.entity.id
| EVAL actor_host_target_id = host.target.entity.id
| EVAL actor_service_target_id = service.target.entity.id
| EVAL actor_entity_target_id = entity.target.id
| EVAL actor_backward_target_id = target.entity.id
| WHERE actor_user_target_id IS NULL AND actor_host_target_id IS NULL AND actor_service_target_id IS NULL AND actor_entity_target_id IS NULL AND actor_backward_target_id IS NOT NULL
| STATS docs_with_any_missing = COUNT(*)
image

classifications test:

query:

FROM logs-*
| WHERE data_stream.dataset == "gcp.audit"
| EVAL actor_user_id           = user.entity.id
| EVAL actor_host_id           = host.entity.id
| EVAL actor_service_id        = service.entity.id
| EVAL actor_entity_id         = entity.id
| EVAL actor_user_target_id    = user.target.entity.id
| EVAL actor_host_target_id    = host.target.entity.id
| EVAL actor_service_target_id = service.target.entity.id
| EVAL actor_entity_target_id  = entity.target.id
| STATS
    actor_user_docs            = COUNT(actor_user_id),
    actor_user_distinct        = COUNT_DISTINCT(actor_user_id),
    actor_user_values          = VALUES(actor_user_id),

    actor_host_docs            = COUNT(actor_host_id),
    actor_host_distinct        = COUNT_DISTINCT(actor_host_id),
    actor_host_values          = VALUES(actor_host_id),

    actor_service_docs         = COUNT(actor_service_id),
    actor_service_distinct     = COUNT_DISTINCT(actor_service_id),
    actor_service_values       = VALUES(actor_service_id),

    actor_entity_docs          = COUNT(actor_entity_id),
    actor_entity_distinct      = COUNT_DISTINCT(actor_entity_id),
    actor_entity_values        = VALUES(actor_entity_id),

    actor_user_target_docs     = COUNT(actor_user_target_id),
    actor_user_target_distinct = COUNT_DISTINCT(actor_user_target_id),
    actor_user_target_values   = VALUES(actor_user_target_id),

    actor_host_target_docs     = COUNT(actor_host_target_id),
    actor_host_target_distinct = COUNT_DISTINCT(actor_host_target_id),
    actor_host_target_values   = VALUES(actor_host_target_id),

    actor_service_target_docs  = COUNT(actor_service_target_id),
    actor_service_target_distinct = COUNT_DISTINCT(actor_service_target_id),
    actor_service_target_values   = VALUES(actor_service_target_id),

    actor_entity_target_docs   = COUNT(actor_entity_target_id),
    actor_entity_target_distinct = COUNT_DISTINCT(actor_entity_target_id),
    actor_entity_target_values   = VALUES(actor_entity_target_id)

results:

{
  "actor_user_docs": 310,
  "actor_user_distinct": 3,
  "actor_user_values": [
    "dmitry.gurevich@elastic.co",
    "user:dmitry.gurevich@elastic.co",
    "paulo.henriquedasilva@elastic.co"
  ],
  "actor_host_docs": 0,
  "actor_host_distinct": 0,
  "actor_service_docs": 617,
  "actor_service_distinct": 16,
  "actor_service_values": [
    "serviceAccount:taggingcleanup-svc-account@elastic-platform-capacity.iam.gserviceaccount.com",
    "taggingcleanup-svc-account@elastic-platform-capacity.iam.gserviceaccount.com",
    "ccb-svc-account@elastic-platform-capacity.iam.gserviceaccount.com",
    "serviceAccount:ccb-svc-account@elastic-platform-capacity.iam.gserviceaccount.com",
    "infosec-seceng-asset-inventory@elastic-infosec.iam.gserviceaccount.com",
    "serviceAccount:infosec-seceng-asset-inventory@elastic-infosec.iam.gserviceaccount.com",
    "qualysreadresources@elastic-infosec.iam.gserviceaccount.com",
    "serviceAccount:qualysreadresources@elastic-infosec.iam.gserviceaccount.com",
    "elastic-agent-cspm-user-sa@elastic-security-test.iam.gserviceaccount.com",
    "serviceAccount:elastic-agent-cspm-user-sa@elastic-security-test.iam.gserviceaccount.com",
    "github-ci@elastic-security-test.iam.gserviceaccount.com",
    "asset-inventory-ci-tests-sa@elastic-security-test.iam.gserviceaccount.com",
    "serviceAccount:asset-inventory-ci-tests-sa@elastic-security-test.iam.gserviceaccount.com",
    "serviceAccount:github-ci@elastic-security-test.iam.gserviceaccount.com",
    "439975565995@cloudservices.gserviceaccount.com",
    "serviceAccount:439975565995@cloudservices.gserviceaccount.com"
  ],
  "actor_entity_docs": 7,
  "actor_entity_distinct": 2,
  "actor_entity_values": [
    "repo:elastic/cloudbeat:ref:refs/heads/main",
    "principal://iam.googleapis.com/projects/439975565995/locations/global/workloadIdentityPools/csp-pool/subject/repo:elastic/cloudbeat:ref:refs/heads/main"
  ],
  "actor_user_target_docs": 0,
  "actor_user_target_distinct": 0,
  "actor_host_target_docs": 10,
  "actor_host_target_distinct": 6,
  "actor_host_target_values": [
    "projects/elastic-security-test/zones/us-central1-c/instances/long-living-gcp-audit-logs",
    "projects/elastic-security-test/zones/us-central1-a/instances/long-living-asset-inventory",
    "projects/elastic-security-test/zones/us-central1-a/instances/live-env-gcp-srvrls",
    "projects/elastic-security-test/zones/us-central1-a/instances/asset-inventory-vm",
    "projects/elastic-security-test/zones/us-central1-a/instances/live-env-cspm-812",
    "projects/elastic-security-test/global/instances"
  ],
  "actor_service_target_docs": 134,
  "actor_service_target_distinct": 103,
  "actor_service_target_values": [
    "projects/-/serviceAccounts/115611681370030074378",
    "projects/-/serviceAccounts/105125502516952720459",
    "projects/-/serviceAccounts/105148845264259647336",
    "projects/-/serviceAccounts/100161087790058397242",
    "projects/-/serviceAccounts/108272665916374788921",
    "projects/-/serviceAccounts/108023747753913599901",
    "projects/-/serviceAccounts/117860656366019987915",
    "projects/-/serviceAccounts/107177173862719407001",
    "projects/-/serviceAccounts/100226343523177193377",
    "projects/-/serviceAccounts/111623253702010814042",
    "projects/-/serviceAccounts/113690620746872368991",
    "projects/-/serviceAccounts/112970163173519767820",
    "projects/-/serviceAccounts/109785468760327654683",
    "projects/-/serviceAccounts/107460672211155015813",
    "projects/-/serviceAccounts/115458948440978066049",
    "projects/-/serviceAccounts/113409123019363075539",
    "projects/-/serviceAccounts/105960906471848789325",
    "projects/-/serviceAccounts/114750693707402979423",
    "projects/-/serviceAccounts/106990567506640336494",
    "projects/-/serviceAccounts/113300941843129210461",
    "projects/elastic-security-test/global/networks",
    "projects/elastic-security-test/global/deployments",
    "projects/elastic-security-test/global/firewalls",
    "projects/elastic-security-test/global/disks",
    "projects/elastic-security-test/global/deployments/proj-main-26nov0352",
    "projects/elastic-security-test/global/networks/proj-main-26nov0352-network",
    "projects/elastic-security-test/global/deployments/proj-main-26nov0352/manifests/empty-manifest-for-delete",
    "projects/elastic-security-test/global/operations/operation-1764170130435-64480ddf2ad5f-24d47c5c-38080586",
    "projects/elastic-security-test/global/networks/elastic-agent-cspm-network",
    "projects/elastic-security-test/global/networks/default",
    "projects/elastic-security-test/global/subnetworks",
    "projects/elastic-security-test/global/operations/operation-1764170087290-64480db60557a-25722c08-c220198a",
    "projects/elastic-security-test/global/firewalls/dg-allow",
    "projects/elastic-security-test/global/firewalls/5368281185407971400",
    "projects/elastic-security-test/policies/gcp.restrictServiceUsage",
    "projects/elastic-security-test/global/deployments/proj-main-26nov0352/resources",
    "projects/elastic-security-test/regions/us-central1/subnetworks/default",
    "projects/elastic-security-test/zones/asia-south2-a/machineTypes",
    "projects/elastic-security-test/zones/asia-south1-c/machineTypes",
    "projects/elastic-security-test/zones/asia-southeast2-b/machineTypes",
    "projects/elastic-security-test/zones/asia-southeast1-a/machineTypes",
    "projects/elastic-security-test/zones/southamerica-west1-b/machineTypes",
    "projects/elastic-security-test/zones/australia-southeast2-b/machineTypes",
    "projects/elastic-security-test/zones/southamerica-west1-a/machineTypes",
    "projects/elastic-security-test/zones/asia-southeast1-b/machineTypes",
    "projects/elastic-security-test/zones/asia-northeast3-a/machineTypes",
    "projects/elastic-security-test/zones/australia-southeast1-b/machineTypes",
    "projects/elastic-security-test/zones/asia-northeast3-c/machineTypes",
    "projects/elastic-security-test/zones/us-west4-b/machineTypes",
    "projects/elastic-security-test/zones/asia-northeast3-b/machineTypes",
    "projects/elastic-security-test/zones/us-west2-c/machineTypes",
    "projects/elastic-security-test/zones/asia-northeast2-c/machineTypes",
    "projects/elastic-security-test/zones/us-west3-a/machineTypes",
    "projects/elastic-security-test/zones/asia-northeast1-c/machineTypes",
    "projects/elastic-security-test/zones/us-east5-c/machineTypes",
    "projects/elastic-security-test/zones/asia-northeast1-b/machineTypes",
    "projects/elastic-security-test/zones/northamerica-south1-a/machineTypes",
    "projects/elastic-security-test/zones/asia-east1-a/machineTypes",
    "projects/elastic-security-test/zones/asia-northeast1-a/machineTypes",
    "projects/elastic-security-test/zones/us-south1-c/machineTypes",
    "projects/elastic-security-test/zones/asia-east1-b/machineTypes",
    "projects/elastic-security-test/zones/us-west3-b/machineTypes",
    "projects/elastic-security-test/zones/us-west3-c/machineTypes",
    "projects/elastic-security-test/zones/us-east5-b/machineTypes",
    "projects/elastic-security-test/zones/us-east5-a/machineTypes",
    "projects/elastic-security-test/zones/northamerica-northeast2-a/machineTypes",
    "projects/elastic-security-test/zones/me-central2-c/machineTypes",
    "projects/elastic-security-test/zones/northamerica-northeast1-a/machineTypes",
    "projects/elastic-security-test/zones/africa-south1-a/machineTypes",
    "projects/elastic-security-test/zones/me-central1-b/machineTypes",
    "projects/elastic-security-test/zones/me-central1-a/machineTypes",
    "projects/elastic-security-test/zones/me-west1-b/machineTypes",
    "projects/elastic-security-test/zones/me-west1-c/machineTypes",
    "projects/elastic-security-test/zones/me-central2-a/machineTypes",
    "projects/elastic-security-test/zones/europe-west8-b/machineTypes",
    "projects/elastic-security-test/zones/us-west1-b/machineTypes",
    "projects/elastic-security-test/zones/europe-west9-a/machineTypes",
    "projects/elastic-security-test/zones/us-east1-b/machineTypes",
    "projects/elastic-security-test/zones/europe-west8-c/machineTypes",
    "projects/elastic-security-test/zones/europe-north1-c/machineTypes",
    "projects/elastic-security-test/zones/europe-north1-b/machineTypes",
    "projects/elastic-security-test/zones/europe-north1-a/machineTypes",
    "projects/elastic-security-test/zones/us-central1-c/machineTypes",
    "projects/elastic-security-test/zones/europe-west9-b/machineTypes",
    "projects/elastic-security-test/zones/europe-west10-c/machineTypes",
    "projects/elastic-security-test/zones/europe-west10-b/machineTypes",
    "projects/elastic-security-test/zones/europe-southwest1-a/machineTypes",
    "projects/elastic-security-test/zones/europe-west12-b/machineTypes",
    "projects/elastic-security-test/zones/us-east1-d/machineTypes",
    "projects/elastic-security-test/zones/europe-central2-c/machineTypes",
    "projects/elastic-security-test/zones/europe-west12-a/machineTypes",
    "projects/elastic-security-test/zones/europe-west12-c/machineTypes",
    "projects/elastic-security-test/zones/europe-north2-c/machineTypes",
    "projects/elastic-security-test/zones/europe-north2-b/machineTypes",
    "projects/elastic-security-test/zones/europe-central2-a/machineTypes",
    "projects/elastic-security-test/zones/us-east4-b/machineTypes",
    "projects/elastic-security-test/zones/us-east4-c/machineTypes",
    "projects/elastic-security-test/zones/europe-west4-b/machineTypes",
    "projects/elastic-security-test/zones/europe-west2-b/machineTypes",
    "projects/elastic-security-test/zones/europe-west4-a/machineTypes",
    "projects/elastic-security-test/zones/europe-west2-c/machineTypes",
    "projects/elastic-security-test/zones/europe-west1-b/machineTypes",
    "projects/elastic-security-test/zones/europe-west1-c/machineTypes"
  ],
  "actor_entity_target_docs": 480,
  "actor_entity_target_distinct": 7,
  "actor_entity_target_values": [
    "projects/439975565995/locations/global/workloadIdentityPools/csp-pool/providers/csp-provider",
    "projects/elastic-security-test",
    "projects/439975565995",
    "managedZones/",
    "projects/elastic-security-test/zones/-",
    "projects/elastic-security-test/global/firewallPolicies",
    "projects/elastic-security-test/global/acceleratorTypes"
  ]
}


fix and add gcp-aduitlogs ingest pipeline tests
@alexreal1314 alexreal1314 self-assigned this Nov 11, 2025
@alexreal1314 alexreal1314 added documentation Improvements or additions to documentation. Applied to PRs that modify *.md files. Integration:gcp Google Cloud Platform Team:Cloud Security Cloud Security team [elastic/cloud-security-posture] labels Nov 11, 2025
@alexreal1314
alexreal1314 marked this pull request as ready for review November 12, 2025 09:25
@alexreal1314
alexreal1314 requested review from a team as code owners November 12, 2025 09:25
@andrewkroh andrewkroh added the Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations] label Nov 12, 2025
@elasticmachine

Copy link
Copy Markdown

Pinging @elastic/security-service-integrations (Team:Security-Service Integrations)

@chrisberkhout chrisberkhout left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice. One minor point in the Painless.

return;
}
String[] parts = path.splitOnToken('.');
def current = ctx;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This assignment is redundant. The function parameter could be named current and could be reassigned in the body without affecting ctx at the call site.

@albertoblaz albertoblaz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Had a quick look and looks good overall. The changes in the Painless script make sense and I saw the support for the new fields while deprecating the old ones.

I just don't dare to accept since I have very little context on specific implementation details

Comment thread packages/gcp/changelog.yml Outdated
@elastic-vault-github-plugin-prod

elastic-vault-github-plugin-prod Bot commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

🚀 Benchmarks report

Package gcp 👍(6) 💚(0) 💔(0)

Expand to view
Data stream Previous EPS New EPS Diff (%) Result
audit 0 1173.71 1173.71 ( - %) 👍
compute 0 14285.71 14285.71 ( - %) 👍
dns 0 2304.15 2304.15 ( - %) 👍
firewall 0 4405.29 4405.29 ( - %) 👍
loadbalancing_logs 0 3215.43 3215.43 ( - %) 👍
vpcflow 0 2976.19 2976.19 ( - %) 👍
@andrewkroh andrewkroh added the enhancement New feature or request label Nov 25, 2025
@alexreal1314
alexreal1314 force-pushed the 14512-gcp-auditlogs-esc-schema branch 2 times, most recently from 3db4393 to fef5900 Compare November 26, 2025 12:32
@alexreal1314
alexreal1314 force-pushed the 14512-gcp-auditlogs-esc-schema branch from fef5900 to ee3a470 Compare November 26, 2025 13:36
@alexreal1314

Copy link
Copy Markdown
Contributor Author

/test benchmark fullreport

@elasticmachine

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @alexreal1314

@alexreal1314 alexreal1314 changed the title [Contextual Security][GCP auditlogs] align entity.* and entity.target.* assignment based on ECS schema updates Nov 28, 2025

@albertoblaz albertoblaz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I've got zero experience with Painless but the whole script and mappings make sense to me

@alexreal1314
alexreal1314 merged commit 95f7e85 into elastic:main Dec 7, 2025
7 checks passed
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

Package gcp - 2.46.0 containing this change is available at https://epr.elastic.co/package/gcp/2.46.0/

@@ -219,6 +219,126 @@ processors:
field: target.entity.id
value: "{{{json.resource.disk_id}}}"
if: ctx.json?.resouce?.disk_id != null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@alexreal1314 is it a typo? resouce and not resource

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation. Applied to PRs that modify *.md files. enhancement New feature or request Integration:gcp Google Cloud Platform Team:Cloud Security Cloud Security team [elastic/cloud-security-posture] Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations]

6 participants