Skip to content

Resolve node.attr.zone via the Node label instead of cloud IMDS - #900

Draft
goat-ssh wants to merge 1 commit into
masterfrom
s/downward-api-zone
Draft

Resolve node.attr.zone via the Node label instead of cloud IMDS#900
goat-ssh wants to merge 1 commit into
masterfrom
s/downward-api-zone

Conversation

@goat-ssh

@goat-ssh goat-ssh commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary of changes

CrateDB's node.attr.zone (used for zone-aware shard allocation) was queried per-provider from each cloud's instance metadata service at 169.254.169.254. That endpoint needs pod egress to survive on all four providers, and it's a classic SSRF/pivot target — closing it off at the NetworkPolicy layer (crate/cloud#3850, crate/cloud#3851) breaks zone detection on every cluster.

Read the pod's own Node object via the in-cluster API server instead: the topology.kubernetes.io/zone label is already populated consistently by AWS, Azure, GCP and STACKIT's cloud-controller-managers. One code path for all four providers, and no pod egress to IMDS required.

Requires a ClusterRole/ClusterRoleBinding granting the cluster's default ServiceAccount read-only access to its own Node object; the binding is per-cluster and explicitly cleaned up on delete, since a namespaced CrateDB CR can't own a cluster-scoped object via owner_references.

Every provider's crate pod restarts once to pick up the new command and NODE_NAME env var.

Checklist

  • Link to issue this PR refers to:
  • Relevant changes are reflected in CHANGES.rst
  • Added or changed code is covered by tests
  • Documentation has been updated if necessary
  • Changed code does not contain any breaking changes (or this is a major version change)

CrateDB's node.attr.zone (used for zone-aware shard allocation) was
queried per-provider from each cloud's instance metadata service at
169.254.169.254. That endpoint needs pod egress to survive on all four
providers, and it's a classic SSRF/pivot target — closing it off at
the NetworkPolicy layer (crate/cloud#3850, crate/cloud#3851) breaks
zone detection on every cluster.

Read the pod's own Node object via the in-cluster API server instead:
the topology.kubernetes.io/zone label is already populated consistently
by AWS, Azure, GCP and STACKIT's cloud-controller-managers. One code
path for all four providers, and no pod egress to IMDS required.

Requires a ClusterRole/ClusterRoleBinding granting the cluster's
default ServiceAccount read-only access to its own Node object; the
binding is per-cluster and explicitly cleaned up on delete, since a
namespaced CrateDB CR can't own a cluster-scoped object via
owner_references.

Every provider's crate pod restarts once to pick up the new command
and NODE_NAME env var.
@goat-ssh
goat-ssh marked this pull request as draft September 4, 2026 13:48
@goat-ssh
goat-ssh requested a lite review from Copilot September 7, 2026 07:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

There are a couple of concrete issues (misleading comment in get_statefulset_crate_env and unconditional ClusterRoleBinding deletion behavior) that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR switches CrateDB zone-aware shard allocation (node.attr.zone) from cloud instance metadata services (IMDS at 169.254.169.254) to reading the pod’s Kubernetes Node label (topology.kubernetes.io/zone) via the in-cluster API server, unifying behavior across AWS/Azure/GCP/STACKIT and removing the need for pod egress to IMDS.

Changes:

  • Replace per-cloud IMDS zone lookup with a single in-cluster Kubernetes Node API lookup for zone-aware providers.
  • Add NODE_NAME env var (Downward API: spec.nodeName) for zone-aware providers to drive the Node lookup.
  • Add RBAC creation for a shared ClusterRole and per-cluster ClusterRoleBinding, plus explicit cleanup logic on cluster deletion; update/extend tests and changelog accordingly.
File summaries
File Description
crate/operator/create.py Implements Node-label-based zone resolution, injects NODE_NAME, and creates ClusterRole/ClusterRoleBinding for Node reads.
crate/operator/handlers/handle_delete_cratedb.py Adds explicit deletion of the per-cluster ClusterRoleBinding on CrateDB deletion.
crate/operator/constants.py Introduces a constant for the shared ClusterRole name.
tests/test_create.py Updates zone-attr tests to assert the new Kubernetes API-based resolution and OpenShift behavior.
tests/test_stackit.py Removes STACKIT IMDS-specific zone tests and adds coverage for NODE_NAME env injection.
CHANGES.rst Documents the new zone-resolution mechanism and networking implications.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +99 to +101
binding_name = f"crate-node-zone-reader-{namespace}-{name}"

async with GlobalApiClient() as api_client:
Comment thread crate/operator/create.py
Comment on lines +721 to +723
# Referenced as ``${NODE_NAME}`` by the ``-Cnode.attr.zone`` Node lookup
# above, on every provider that resolves zone awareness that way.
if config.CLOUD_PROVIDER in (
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.

2 participants