EXPLORE
← Back to Explore
elasticmediumTTP

GKE Secret Access from Node or Denied Service Account

Detects GKE Secrets API activity that should not occur in normal cluster operation: a node identity (system:node:*) performing secrets get or list, or a pod service account failing a secrets get. Kubelet and node credentials are not expected to call the Secrets API for enumeration or direct reads, and a denied service-account secret get could indicate stolen-token probing or over-privileged tooling reaching beyond its RBAC.

MITRE ATT&CK

credential-access

Detection Query

data_stream.dataset:gcp.audit and service.name:k8s.io and
source.ip:(* and not (127.0.0.1 or "::1")) and
(
  (
    client.user.email:system\:node\:* and
    event.action:(io.k8s.core.v1.secrets.get or io.k8s.core.v1.secrets.list)
  ) or (
    client.user.email:system\:serviceaccount\:* and
    event.action:io.k8s.core.v1.secrets.get and
    event.outcome:failure
  )
)

Author

Elastic

Created

2026/07/17

Data Sources

GCPGoogle Cloud Platformlogs-gcp.audit-*

Tags

Domain: CloudDomain: KubernetesData Source: GCPData Source: Google Cloud PlatformUse Case: Threat DetectionTactic: Credential AccessResources: Investigation Guide
Raw Content
[metadata]
creation_date = "2026/07/17"
integration = ["gcp"]
maturity = "production"
updated_date = "2026/07/17"

[rule]
author = ["Elastic"]
description = """
Detects GKE Secrets API activity that should not occur in normal cluster operation: a node identity
(system:node:*) performing secrets get or list, or a pod service account failing a secrets get.
Kubelet and node credentials are not expected to call the Secrets API for enumeration or direct reads,
and a denied service-account secret get could indicate stolen-token probing or over-privileged tooling
reaching beyond its RBAC.
"""
false_positives = [
    """
    Rare node maintenance or diagnostic tooling may touch the Secrets API; validate against change windows
    and approved node management paths.
    """,
    """
    Misconfigured workloads can generate repeated denied secret gets during startup; confirm RBAC intent
    before treating as compromise.
    """,
]
from = "now-6m"
index = ["logs-gcp.audit-*"]
language = "kuery"
license = "Elastic License v2"
name = "GKE Secret Access from Node or Denied Service Account"
note = """## Triage and analysis

### Investigating GKE Secret Access from Node or Denied Service Account

This rule fires on two high-confidence patterns in GKE audit logs:

- `system:node:*` successfully or unsuccessfully calling `secrets.get` or `secrets.list`
- `system:serviceaccount:*` receiving `event.outcome:failure` on `secrets.get`

Treat node-originated Secrets API calls as priority. For denied service-account gets, determine whether
the identity is probing secrets outside its Role/ClusterRole or using an unexpected client from a
compromised token.

### Possible investigation steps

- Resolve `client.user.email` to the node or workload and review RBAC bindings for secret `get`/`list` scope.
- Inspect `gcp.audit.resource_name`, `source.ip`, and `user_agent.original` for anomalous clients or
  cross-namespace targets.
- Correlate with successful secret reads, pod exec, token creation, or RoleBinding changes in the same window.
- For node events, confirm whether kubelet, CSI, or maintenance tooling on that node should ever call the
  Secrets API.

### False positive analysis

- Node diagnostics or custom CSI helpers may rarely issue Secrets API calls; allowlist only after confirming
  the path is approved.
- Broken RBAC on a newly deployed workload can produce repeated denied gets until permissions are fixed.

### Response and remediation

- If malicious, revoke the node or service-account credentials, isolate the host or workload, rotate exposed
  secrets, and tighten RBAC to least privilege for the affected identity.
"""
setup = "The GCP Fleet integration with GKE audit logs enabled is required to be compatible with this rule."
references = [
    "https://kubernetes.io/docs/reference/access-authn-authz/authentication/#service-account-tokens",
]
risk_score = 47
rule_id = "bedab6b4-f195-45d1-8f97-7d300eb79671"
severity = "medium"
tags = [
    "Domain: Cloud",
    "Domain: Kubernetes",
    "Data Source: GCP",
    "Data Source: Google Cloud Platform",
    "Use Case: Threat Detection",
    "Tactic: Credential Access",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "query"

query = '''
data_stream.dataset:gcp.audit and service.name:k8s.io and
source.ip:(* and not (127.0.0.1 or "::1")) and
(
  (
    client.user.email:system\:node\:* and
    event.action:(io.k8s.core.v1.secrets.get or io.k8s.core.v1.secrets.list)
  ) or (
    client.user.email:system\:serviceaccount\:* and
    event.action:io.k8s.core.v1.secrets.get and
    event.outcome:failure
  )
)
'''

[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1552"
name = "Unsecured Credentials"
reference = "https://attack.mitre.org/techniques/T1552/"

[[rule.threat.technique.subtechnique]]
id = "T1552.007"
name = "Container API"
reference = "https://attack.mitre.org/techniques/T1552/007/"

[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"

[rule.investigation_fields]
field_names = [
    "@timestamp",
    "client.user.email",
    "source.ip",
    "user_agent.original",
    "event.action",
    "event.outcome",
    "gcp.audit.resource_name",
    "data_stream.namespace",
]