EKS ebs-csi add-on defaultStorageClass fights the encrypted default StorageClass (two-defaults on reconcile)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 7
- Forks
- 2
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 3
Description
Summary
The workload eks step (and the AWSEKSCluster builder it shares) configures the EKS
ebs-csi managed add-on with defaultStorageClass.enabled: true. That add-on feature
creates a StorageClass named ebs-csi-default-sc (gp3, no encrypted parameter) and marks
it the cluster default. The step then creates ebs-csi-default-sc-encrypted (encrypted: true, marked default) and patches ebs-csi-default-sc to is-default-class=false, so
the encrypted class is the real cluster default.
This works as long as the add-on is never reconciled — but any add-on update makes EKS
re-reconcile the add-on, and its defaultStorageClass feature re-asserts
is-default-class=true on ebs-csi-default-sc, overriding the patch. The result is two
default StorageClasses, which the cluster resolves non-deterministically.
Why it matters
- Two defaults → a PVC that omits
storageClassNamemay bind to either class. - The add-on's
ebs-csi-default-scsets noencryptedparameter. In accounts where
EBS encryption-by-default is disabled, the SC-levelencrypted: trueon the encrypted
class is the only thing encrypting volumes — so a PVC that lands onebs-csi-default-sc
there would be unencrypted. (We have confirmed a mix in the fleet: some accounts have
account-level default encryption on, several production accounts have it off. So the
encrypted default SC is genuinely load-bearing and cannot be dropped or replaced by relying
on account-level encryption.) ebs-csi-default-scitself is load-bearing on a subset of clusters: the in-cluster
observability stack (Loki/Mimir) pinsstorageClassName: ebs-csi-default-scexplicitly, so
the class must be preserved (cannot be blanket-deleted).
How this surfaced
During the Python→Go migration of the eks step, the Go code re-serialized the add-on's
configurationValues (Go's compact JSON vs the prior spaced JSON — identical content,
different bytes). That byte difference alone triggered an add-on update → reconcile → the
flip above, producing two default StorageClasses on the clusters it was applied to. (An
earlier attempt to set the add-on's update conflict-resolution to PRESERVE let the update
succeed but did not prevent the flip — the add-on recreates/re-asserts its default SC,
so PRESERVE is not a fix.) The control-room cluster step is unaffected: it intentionally
uses ebs-csi-default-sc as its default and applies no demoting patch, so there is no
conflict there. This is workload-only.
Current mitigation (in place)
pulumi.IgnoreChanges(["configurationValues"]) on the ebs-csi (and secrets-store) add-ons.
The migration adopts the live add-on config exactly as-is and never re-serializes/updates it,
so EKS never reconciles the add-on and never flips the default. The existing, correct state
(encrypted class = sole default; ebs-csi-default-sc present but demoted) is preserved.
What to expect for a NEW workload while this mitigation is in place
- Greenfield create: the add-on is created with
defaultStorageClass.enabled: true→
createsebs-csi-default-scas default → the encrypted class + the demoting patch then make
the encrypted class the sole default.IgnoreChangesonly affects updates, not create, so
provisioning is unaffected and the workload comes up correctly (one default = encrypted). - Steady state: because the add-on's
configurationValuesis never re-applied, the add-on
is never reconciled, so the default never flips. Stable. - Residual fragility (unchanged from before the migration): an explicit add-on version
bump is a real add-on update (the version field is not ignored) → EKS reconciles → the
default flips back toebs-csi-default-sc→ two defaults, and the demoting patch (a separate
resource) will not automatically re-apply. Remediation today is a one-time
kubectl annotate sc ebs-csi-default-sc storageclass.kubernetes.io/is-default-class=false --overwrite. This is the same latent issue that existed pre-migration; the mitigation just
stops the migration itself from triggering it.
Proper long-term fix
Stop using the add-on's defaultStorageClass feature and have PTD own the StorageClasses
directly:
- Set the ebs-csi add-on
defaultStorageClass.enabled: falseso the add-on no longer
creates/manages a default class. - Define
ebs-csi-default-scourselves as a plain non-default StorageClass (so the
Loki/Mimir PVCs that pin it keep resolving). - Keep
ebs-csi-default-sc-encryptedas the sole default.
Then there is no add-on-managed default to fight over — no demoting patch, and no flip on
add-on version bumps. Transition risk to validate first: flipping enabled: false on an
existing add-on may delete the add-on-created ebs-csi-default-sc (load-bearing for the
observability stack on some clusters). Confirm the delete-vs-orphan behavior on a safe target
and define the ownership hand-off (create our SC and transfer field management) before rolling
this out.
Optional interim hardening: make the demoting StorageClassPatch dependsOn the add-on and
re-assert on every apply, so even a version-bump reconcile self-heals.
References
- Mitigation lands with the eks/cluster Python→Go migration PR.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the workload eks step and the shared AWSEKSCluster builder, then inspect the ebs-csi add-on configuration and the StorageClassPatch that demotes ebs-csi-default-sc. Validate on a safe target whether disabling defaultStorageClass deletes or orphans that class, especially where Loki/Mimir pins it. Done means PTD owns a non-default ebs-csi-default-sc, the encrypted class is the sole default, and add-on updates no longer create two defaults.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, go, kubernetes
- Domain
- cloud, devops, infrastructure
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100