fix: require iam:PassRole for EKS add-on service account role

CreateAddon and UpdateAddon now pass serviceAccountRoleArn through the
existing EKS PassedRoleARNs path, so the caller needs iam:PassRole on the
role for eks.amazonaws.com, matching CreateCluster and CreateNodegroup.
This commit is contained in:
joshsiv-mulga
2026-10-08 22:40:30 +10:00
parent 675341b561
commit 097e19109a
3 changed files with 69 additions and 5 deletions
+12
View File
@@ -23,6 +23,18 @@ func PassedRoleARNs(action string, body []byte) []string {
return nil
}
roleARN = input.NodeRole
case "CreateAddon":
input := new(eks.CreateAddonInput)
if unmarshalIfBody(body, input) != nil {
return nil
}
roleARN = input.ServiceAccountRoleArn
case "UpdateAddon":
input := new(eks.UpdateAddonInput)
if unmarshalIfBody(body, input) != nil {
return nil
}
roleARN = input.ServiceAccountRoleArn
}
if aws.StringValue(roleARN) == "" {
return nil
+11 -5
View File
@@ -78,12 +78,18 @@ func TestEKSRequest_InternalAddonsWireBody(t *testing.T) {
rec.Body.String())
}
// Only CreateCluster and CreateNodegroup hand a role to iam:PassRole; the add-on
// service-account role is stored and rendered without that check.
func TestPassedRoleARNs_AddonServiceAccountRoleIsNotChecked(t *testing.T) {
// CreateAddon and UpdateAddon hand their service-account role to iam:PassRole,
// same as CreateCluster's roleArn and CreateNodegroup's nodeRole; an absent
// role passes nothing.
func TestPassedRoleARNs_AddonServiceAccountRoleIsChecked(t *testing.T) {
body := []byte(`{"addonName":"aws-load-balancer-controller","serviceAccountRoleArn":"arn:aws:iam::123456789012:role/alb"}`)
assert.Empty(t, gateway_eks.PassedRoleARNs("CreateAddon", body))
assert.Empty(t, gateway_eks.PassedRoleARNs("UpdateAddon", body))
assert.Equal(t, []string{"arn:aws:iam::123456789012:role/alb"}, gateway_eks.PassedRoleARNs("CreateAddon", body))
assert.Equal(t, []string{"arn:aws:iam::123456789012:role/alb"}, gateway_eks.PassedRoleARNs("UpdateAddon", body))
roleless := []byte(`{"addonName":"spinifex-noop"}`)
assert.Empty(t, gateway_eks.PassedRoleARNs("CreateAddon", roleless))
assert.Empty(t, gateway_eks.PassedRoleARNs("UpdateAddon", roleless))
assert.Equal(t, []string{"arn:aws:iam::123456789012:role/node"},
gateway_eks.PassedRoleARNs("CreateNodegroup", []byte(`{"nodeRole":"arn:aws:iam::123456789012:role/node"}`)))
}
+46
View File
@@ -160,3 +160,49 @@ func TestEKSCreateCluster_PassRole(t *testing.T) {
)
assertPermitted(t, dispatchEKS(t, allowed, http.MethodPost, "/clusters", body))
}
func createAddonBody(roleARN string) string {
return `{"addonName":"aws-load-balancer-controller","serviceAccountRoleArn":"` + roleARN + `"}`
}
func updateAddonBody(roleARN string) string {
return `{"serviceAccountRoleArn":"` + roleARN + `"}`
}
func TestEKSCreateAddon_PassRole(t *testing.T) {
body := createAddonBody(eksNodeRoleARN)
denied := eksPassRoleGateway(statement("Allow", "eks:*", "*"))
assertDenied(t, dispatchEKS(t, denied, http.MethodPost, "/clusters/prod/addons", body))
allowed := eksPassRoleGateway(
statement("Allow", "eks:*", "*"),
statement("Allow", "iam:PassRole", eksNodeRoleARN),
)
assertPermitted(t, dispatchEKS(t, allowed, http.MethodPost, "/clusters/prod/addons", body))
}
func TestEKSCreateAddon_NoRoleSkipsPassRole(t *testing.T) {
gw := eksPassRoleGateway(statement("Allow", "eks:*", "*"))
assertPermitted(t, dispatchEKS(t, gw, http.MethodPost, "/clusters/prod/addons", createAddonBody("")))
}
func TestEKSUpdateAddon_PassRole(t *testing.T) {
body := updateAddonBody(eksNodeRoleARN)
const path = "/clusters/prod/addons/aws-load-balancer-controller/update"
denied := eksPassRoleGateway(statement("Allow", "eks:*", "*"))
assertDenied(t, dispatchEKS(t, denied, http.MethodPost, path, body))
allowed := eksPassRoleGateway(
statement("Allow", "eks:*", "*"),
statement("Allow", "iam:PassRole", eksNodeRoleARN),
)
assertPermitted(t, dispatchEKS(t, allowed, http.MethodPost, path, body))
}
func TestEKSUpdateAddon_NoRoleSkipsPassRole(t *testing.T) {
gw := eksPassRoleGateway(statement("Allow", "eks:*", "*"))
assertPermitted(t, dispatchEKS(t, gw, http.MethodPost,
"/clusters/prod/addons/aws-load-balancer-controller/update", updateAddonBody("")))
}