GKE Secret Access from Node or Denied Service Account


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.

Query · kuery

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
  )
)

Investigation fields

Pivot points the source recommends for triage.

  • @timestamp
  • client.user.email
  • source.ip
  • user_agent.original
  • event.action
  • event.outcome
  • gcp.audit.resource_name
  • data_stream.namespace

Implementation guide

The GCP Fleet integration with GKE audit logs enabled is required to be compatible with this rule.

Known 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.

Analyst notes

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.
Raw source GKE Secret Access from Node or Denied Service Account · Elastic TOML
Esc
Published by elastic/detection-rules ↗, licensed under Elastic License 2.0 ↗. Reproduced here unmodified.
[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",
]

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.