Skip to content

feat(eks): run the runtime-plugin sandbox pool on EKS - #2532

Merged
Brad-Edwards merged 7 commits into
devfrom
fix/2526-runtime-plugin-pool-eks
Oct 11, 2026
Merged

Brad-Edwards merged 7 commits into
devfrom
fix/2526-runtime-plugin-pool-eks

Conversation

@Brad-Edwards

@Brad-Edwards Brad-Edwards commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Runtime-plugin worker Jobs select node-restriction.kubernetes.io/shifter-pool=runtime-plugin. NodeRestriction stops a kubelet from setting that label, which is what makes it a trustworthy pool identity. GKE applies it through its control plane. EKS managed node groups apply labels through the kubelet, so the pool could never be populated on EKS.

  • Terraform declares a dedicated gVisor node group with its own node role, the plugin taint, IMDSv2 with hop limit 1 and an encrypted root volume. aws-dev enables it.
  • A trusted controller (shared.cloud.eks_node_pool_labeler) labels a node only when its EC2 instance is running, its private DNS name equals the node name, and its AWS-owned aws:autoscaling:groupName tag names the pool's Auto Scaling group. A node that fails the check loses the label; a provider error leaves the node unchanged. Its IRSA role can only call ec2:DescribeInstances. (An earlier instance-profile check was dropped because the CI permissions boundary denies iam:GetInstanceProfile.)
  • The chart adds the labeler's service account, its node RBAC, a NetworkPolicy limited to Kubernetes API egress, and a ValidatingAdmissionPolicy that limits the service account to toggling the one pool label. The AWS bootstrap renderer enables the labeler only when both the pool and the labeler identity are present, and fails if only one is.
  • Resources are sized from the measured footprint: about 180 MiB, so 192Mi request and 320Mi limit.

Closes #2526

Verification

  • Focused tests pass: labeler (4), bootstrap renderer, chart contract, and terraform test -filter=tests/security.tftest.hcl (3).
  • Deployed on aws-dev. The runtime-plugin node ip-10-80-20-57 joined Ready and is the only node with shifter-pool=runtime-plugin; the labeler logged node pool label applied.
  • A pod with the worker profile's runtime class, selector and toleration scheduled onto ip-10-80-20-57 and ran sandboxed (dmesg: Starting gVisor..., kernel 4.19.0-gvisor).

… EKS

Runtime-plugin worker Jobs select node-restriction.kubernetes.io/shifter-pool=
runtime-plugin under gVisor. NodeRestriction forbids a kubelet from setting that
label, and EKS managed node groups apply labels through the kubelet, so the EKS
pool could not join and stayed disabled; range launch for any adapter-bound pack
then timed out waiting for plugin plans.

- The pool gets its own node role and no kubelet-set labels.
- shared.cloud.aws.node_pool_labeler labels a node only when its EC2 instance is
  running, its private DNS name is the node's authenticated name, and its
  instance profile carries the pool's node role; it removes the label from any
  node that fails the check and leaves nodes alone on provider errors.
- The chart runs it (AWS only) with an IRSA identity limited to
  ec2:DescribeInstances and iam:GetInstanceProfile on EKS-created profiles, a
  nodes-only ClusterRole, Kubernetes-API-only egress, and an admission policy
  that lets its identity toggle the pool label and nothing else.
- The AWS bootstrap renders it from the nodePoolLabeler identity and the pool's
  node role, failing when only one is present; aws-dev enables the pool.

Refs #2526
The labeler is a standalone EKS controller, not a cloud adapter with a GCP
counterpart (ADR-005-R1 cloud-factory seam): it lives beside the other
standalone cloud programs as shared.cloud.eks_node_pool_labeler.

Refs #2526
…lients

SonarCloud flagged the labeler's Any-typed client parameters and a five-return
lookup. Small Protocols and TypedDicts describe exactly the EC2, IAM and Node
fields it reads, and the instance lookup is split so each function has a single
decision.

Refs #2526
The labeler was OOMKilled at its 128Mi limit on aws-dev: the platform package,
boto3 and the Kubernetes client take about 180 MiB resident before the first
reconcile. Request 192Mi and allow 320Mi.

Refs #2526
The node-pool labeler resolved each node's instance profile to its role, but
the CI permissions boundary denies iam:GetInstanceProfile, so every check
deferred and no node was ever labeled. Pool membership is now the AWS-owned
aws:autoscaling:groupName tag matching the runtime-plugin node group's ASG,
together with a running state and a private DNS name equal to the node name.
The labeler's IAM grant shrinks to ec2:DescribeInstances.
@sonarqubecloud

Copy link
Copy Markdown

@Brad-Edwards
Brad-Edwards merged commit 2c2ab84 into dev Oct 11, 2026
116 checks passed
@Brad-Edwards
Brad-Edwards deleted the fix/2526-runtime-plugin-pool-eks branch October 11, 2026 19:44
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.

1 participant