Skip to content

Tighten resource attribution in the Stackdriver event sink - #1186

Merged
erain merged 1 commit into
GoogleCloudPlatform:masterfrom
erain:event-resource-attribution-hardening
May 30, 2026
Merged

Tighten resource attribution in the Stackdriver event sink#1186
erain merged 1 commit into
GoogleCloudPlatform:masterfrom
erain:event-resource-attribution-hardening

Conversation

@erain

@erain erain commented May 12, 2026

Copy link
Copy Markdown
Contributor

Summary

When the event-exporter maps a corev1.Event onto a Stackdriver MonitoredResource, the Pod/Node labels are taken from the event's involvedObject reference. These reference fields are not cross-checked against the event's own metadata.namespace, which is the value the API server validates when an event is created.

This change adds that cross-check inside resourceFromEvent:

  • Pod events must satisfy event.Namespace != \"\" and event.Namespace == event.InvolvedObject.Namespace. When the check passes, namespace_name is sourced from event.Namespace directly. Otherwise the entry falls back to the default cluster-scoped resource.
  • Node events (which have no per-event namespace to compare against) are attributed to k8s_node only when the event originates from one of default, kube-system, or kube-node-lease. Otherwise the entry falls back to the default cluster-scoped resource.

The fallback preserves the log content under the cluster resource, so events that don't pass the check are still exported and inspectable; they just stop being attributed to the workload they reference.

Test plan

  • go test -mod=vendor ./... in event-exporter/ is green.
  • monitored_resource_factory_test.go gains coverage for: pod event whose event namespace disagrees with the involved object, pod event with empty event namespace, node event in trusted namespace, node event in untrusted namespace.
  • log_entry_factory_test.go cases updated to populate event.Namespace consistent with the new requirement; pod-label enrichment path still exercised end-to-end.

@erain

erain commented May 13, 2026

Copy link
Copy Markdown
Contributor Author

Pushed f85462c to address the review feedback:

  • Dropped default from the trusted set in monitored_resource_factory.go; only kube-system and kube-node-lease now produce a node-scoped MonitoredResource. Pod attribution is unchanged (still requires event.Namespace != "" and event.Namespace == event.InvolvedObject.Namespace, with namespace_name sourced from event.Namespace).
  • monitored_resource_factory_test.go: the previous defaultk8s_node case now asserts the cluster fallback; a kube-node-leasek8s_node case is added so both trusted namespaces are covered.
  • log_entry_factory_test.go: node fixtures switched from default to kube-system so the trusted-namespace path is still exercised.

go test -mod=vendor ./... from event-exporter/:

?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter	[no test files]
ok  	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/kubernetes/podlabels	(cached)
?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/kubernetes/watchers	[no test files]
ok  	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/kubernetes/watchers/events	(cached)
?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/sinks	[no test files]
ok  	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/sinks/stackdriver	0.820s
?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/utils	[no test files]

@JeffLuoo

Copy link
Copy Markdown
Contributor

/lgtm
/approve

When mapping a corev1.Event onto a Stackdriver MonitoredResource with
InvolvedObject.Kind == "Pod", only emit a k8s_pod resource when the
event's own metadata.namespace is set and equals
event.InvolvedObject.Namespace. Use event.Namespace (the value the API
server validates against RBAC at event creation time) to populate the
namespace_name label. Events that fail the check fall back to the
default cluster-scoped resource so the log content is still exported.

Node attribution is intentionally unchanged in this PR; it will be
addressed separately.

Updates the existing pod test to populate event.Namespace and adds
cases that cover the new fallback paths.
@erain
erain force-pushed the event-resource-attribution-hardening branch from f85462c to 208ca39 Compare May 22, 2026 20:21
@erain

erain commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

Force-pushed 208ca39 to scope this PR strictly to Pod attribution:

  • Removed trustedNodeEventNamespaces and reverted resourceFromEvent so the Node case goes straight to buildNodeMonitoredResource(event) exactly as on master. Node attribution will be addressed in a follow-up PR.
  • Kept the Pod fix: k8s_pod is emitted only when event.Namespace != "" and event.Namespace == event.InvolvedObject.Namespace; namespace_name is sourced from event.Namespace; otherwise fall back to the cluster resource.
  • monitored_resource_factory_test.go: keeps the matching-namespace pod case, adds mismatched-namespace and empty-event-namespace fallback cases; node-allowlist cases removed.
  • log_entry_factory_test.go: only the pod fixture gains ObjectMeta.Namespace; node fixtures are back to their original (no namespace) form.

go test -mod=vendor ./... from event-exporter/:

?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter	[no test files]
ok  	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/kubernetes/podlabels	(cached)
?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/kubernetes/watchers	[no test files]
ok  	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/kubernetes/watchers/events	(cached)
?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/sinks	[no test files]
ok  	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/sinks/stackdriver	0.829s
?   	github.com/GoogleCloudPlatform/k8s-stackdriver/event-exporter/utils	[no test files]

@erain
erain requested a review from courageJ May 28, 2026 19:01
@erain
erain merged commit 400e1b3 into GoogleCloudPlatform:master May 30, 2026
18 checks passed
@erain
erain deleted the event-resource-attribution-hardening branch May 30, 2026 02:15
erain added a commit to erain/fluent-bit that referenced this pull request Jul 1, 2026
The logging.googleapis.com/local_resource_id field is read from the log
payload to derive the k8s_container/k8s_pod/k8s_node monitored resource
labels. In multi-tenant pipelines the payload is workload-controlled, so
a compromised or malicious workload can forge this field and attribute
its log entries to another namespace/pod/container across trust
boundaries.

Add a trust_payload_local_resource_id option (default: true, preserving
existing behavior). When set to false:

- the payload local_resource_id is ignored; resource labels are only
  derived from the tag (via tag_prefix + regex), configured
  resource_labels, or a payload monitored resource map.
- when none of those trusted sources is available, entries fall back to
  the cluster scoped k8s_cluster resource instead of packing workload
  labels forged from the payload.

This mirrors the hardening previously applied to the Go based logging
agent in GoogleCloudPlatform/k8s-stackdriver#1186.

Signed-off-by: Yu Yi <yiyu@google.com>
erain added a commit to erain/fluent-bit that referenced this pull request Jul 9, 2026
The logging.googleapis.com/local_resource_id field is read from the log
payload to derive the k8s_container/k8s_pod/k8s_node monitored resource
labels. In multi-tenant pipelines the payload is workload-controlled, so
a compromised or malicious workload can forge this field and attribute
its log entries to another namespace/pod/container across trust
boundaries.

Add a trust_payload_local_resource_id option (default: true, preserving
existing behavior). When set to false:

- the payload local_resource_id is ignored; resource labels are only
  derived from the tag (via tag_prefix + regex), configured
  resource_labels, or a payload monitored resource map.
- when none of those trusted sources is available, entries fall back to
  the cluster scoped k8s_cluster resource instead of packing workload
  labels forged from the payload.

This mirrors the hardening previously applied to the Go based logging
agent in GoogleCloudPlatform/k8s-stackdriver#1186.

Signed-off-by: Yu Yi <yiyu@google.com>
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.

3 participants