Skip to content

Commit bae312d

Browse files
committed
Add eks pod identities
1 parent 85a3d6f commit bae312d

18 files changed

+753
-11
lines changed

config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2637,6 +2637,35 @@ spec:
26372637
description: Partition is the AWS security partition being used. Defaults
26382638
to "aws"
26392639
type: string
2640+
podIdentityAssociations:
2641+
description: |-
2642+
PodIdentityAssociations represent Kubernetes Service Accounts mapping to AWS IAM Roles without IRSA, using EKS Pod Identity.
2643+
This requires using the AWS EKS Addon for Pod Identity.
2644+
items:
2645+
description: PodIdentityAssociation represents an association between
2646+
a Kubernetes Service Account in a namespace, and an AWS IAM role
2647+
which allows the service principal `pods.eks.amazonaws.com` in
2648+
its trust policy.
2649+
properties:
2650+
serviceAccountName:
2651+
description: ServiceAccountName is the name of the kubernetes
2652+
Service Account within the namespace
2653+
type: string
2654+
serviceAccountNamespace:
2655+
default: default
2656+
description: ServiceAccountNamespace is the kubernetes namespace,
2657+
which the kubernetes Service Account resides in. Defaults
2658+
to "default" namespace.
2659+
type: string
2660+
serviceAccountRoleARN:
2661+
description: RoleARN is the ARN of an IAM role which the Service
2662+
Account can assume.
2663+
type: string
2664+
required:
2665+
- serviceAccountName
2666+
- serviceAccountNamespace
2667+
type: object
2668+
type: array
26402669
region:
26412670
description: The AWS Region the cluster lives in.
26422671
type: string

controlplane/eks/api/v1beta1/conversion.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func (r *AWSManagedControlPlane) ConvertTo(dstRaw conversion.Hub) error {
4040
}
4141
dst.Spec.VpcCni.Disable = r.Spec.DisableVPCCNI
4242
dst.Spec.Partition = restored.Spec.Partition
43+
dst.Spec.PodIdentityAssociations = restored.Spec.PodIdentityAssociations
4344

4445
return nil
4546
}

controlplane/eks/api/v1beta1/zz_generated.conversion.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ type AWSManagedControlPlaneSpec struct { //nolint: maligned
164164
// +optional
165165
Addons *[]Addon `json:"addons,omitempty"`
166166

167+
// PodIdentityAssociations represent Kubernetes Service Accounts mapping to AWS IAM Roles without IRSA, using EKS Pod Identity.
168+
// This requires using the AWS EKS Addon for Pod Identity.
169+
// +optional
170+
PodIdentityAssociations []PodIdentityAssociation `json:"podIdentityAssociations,omitempty"`
171+
167172
// IdentityProviderconfig is used to specify the oidc provider config
168173
// to be attached with this eks cluster
169174
// +optional

controlplane/eks/api/v1beta2/awsmanagedcontrolplane_webhook.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/pkg/errors"
2525
apierrors "k8s.io/apimachinery/pkg/api/errors"
2626
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/apimachinery/pkg/util/validation"
2728
"k8s.io/apimachinery/pkg/util/validation/field"
2829
"k8s.io/apimachinery/pkg/util/version"
2930
"k8s.io/klog/v2"
@@ -63,8 +64,10 @@ func (r *AWSManagedControlPlane) SetupWebhookWithManager(mgr ctrl.Manager) error
6364
// +kubebuilder:webhook:verbs=create;update,path=/validate-controlplane-cluster-x-k8s-io-v1beta2-awsmanagedcontrolplane,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=controlplane.cluster.x-k8s.io,resources=awsmanagedcontrolplanes,versions=v1beta2,name=validation.awsmanagedcontrolplanes.controlplane.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
6465
// +kubebuilder:webhook:verbs=create;update,path=/mutate-controlplane-cluster-x-k8s-io-v1beta2-awsmanagedcontrolplane,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=controlplane.cluster.x-k8s.io,resources=awsmanagedcontrolplanes,versions=v1beta2,name=default.awsmanagedcontrolplanes.controlplane.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1
6566

66-
var _ webhook.Defaulter = &AWSManagedControlPlane{}
67-
var _ webhook.Validator = &AWSManagedControlPlane{}
67+
var (
68+
_ webhook.Defaulter = &AWSManagedControlPlane{}
69+
_ webhook.Validator = &AWSManagedControlPlane{}
70+
)
6871

6972
func parseEKSVersion(raw string) (*version.Version, error) {
7073
v, err := version.ParseGeneric(raw)
@@ -95,6 +98,7 @@ func (r *AWSManagedControlPlane) ValidateCreate() (admission.Warnings, error) {
9598
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
9699
allErrs = append(allErrs, r.validateNetwork()...)
97100
allErrs = append(allErrs, r.validatePrivateDNSHostnameTypeOnLaunch()...)
101+
allErrs = append(allErrs, r.validServiceAccountName()...)
98102

99103
if len(allErrs) == 0 {
100104
return nil, nil
@@ -129,6 +133,7 @@ func (r *AWSManagedControlPlane) ValidateUpdate(old runtime.Object) (admission.W
129133
allErrs = append(allErrs, r.validateKubeProxy()...)
130134
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
131135
allErrs = append(allErrs, r.validatePrivateDNSHostnameTypeOnLaunch()...)
136+
allErrs = append(allErrs, r.validServiceAccountName()...)
132137

133138
if r.Spec.Region != oldAWSManagedControlplane.Spec.Region {
134139
allErrs = append(allErrs,
@@ -403,6 +408,28 @@ func (r *AWSManagedControlPlane) validatePrivateDNSHostnameTypeOnLaunch() field.
403408
return allErrs
404409
}
405410

411+
func (r *AWSManagedControlPlane) validServiceAccountName() field.ErrorList {
412+
var allErrs field.ErrorList
413+
414+
if r.Spec.PodIdentityAssociations != nil {
415+
for i, association := range r.Spec.PodIdentityAssociations {
416+
associationPath := field.NewPath("spec", "podIdentityAssociations").Index(i)
417+
if association.ServiceAccountName == "" {
418+
allErrs = append(allErrs, field.Required(associationPath.Child("serviceAccountName"), "serviceAccountName is required"))
419+
}
420+
421+
// kubernetes uses ValidateServiceAccountName internally, which maps to IsDNS1123Subdomain
422+
// https://github.com/kubernetes/apimachinery/blob/d794766488ac2892197a7cc8d0b4b46b0edbda80/pkg/api/validation/generic.go#L68
423+
424+
if validationErrs := validation.IsDNS1123Subdomain(association.ServiceAccountName); len(validationErrs) > 0 {
425+
allErrs = append(allErrs, field.Invalid(associationPath.Child("serviceAccountName"), association.ServiceAccountName, fmt.Sprintf("serviceAccountName is invalid: %v", validationErrs)))
426+
}
427+
}
428+
}
429+
430+
return allErrs
431+
}
432+
406433
func (r *AWSManagedControlPlane) validateNetwork() field.ErrorList {
407434
var allErrs field.ErrorList
408435

controlplane/eks/api/v1beta2/conditions_consts.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ const (
5252
EKSAddonsConfiguredFailedReason = "EKSAddonsConfiguredFailed"
5353
)
5454

55+
const (
56+
// EKSPodIdentityAssociationConfiguredCondition condition reports on the successful reconciliation of EKS pod identity associations.
57+
EKSPodIdentityAssociationConfiguredCondition clusterv1.ConditionType = "EKSPodIdentityAssociationConfigured"
58+
// EKSPodIdentityAssociationFailedReason is used to report failures while reconciling the EKS pod identity associations.
59+
EKSPodIdentityAssociationFailedReason = "EKSPodIdentityAssociationConfigurationFailed"
60+
)
61+
5562
const (
5663
// EKSIdentityProviderConfiguredCondition condition reports on the successful association of identity provider config.
5764
EKSIdentityProviderConfiguredCondition clusterv1.ConditionType = "EKSIdentityProviderConfigured"

controlplane/eks/api/v1beta2/types.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,10 @@ var (
7979
EKSTokenMethodAWSCli = EKSTokenMethod("aws-cli")
8080
)
8181

82-
var (
83-
// DefaultEKSControlPlaneRole is the name of the default IAM role to use for the EKS control plane
84-
// if no other role is supplied in the spec and if iam role creation is not enabled. The default
85-
// can be created using clusterawsadm or created manually.
86-
DefaultEKSControlPlaneRole = fmt.Sprintf("eks-controlplane%s", iamv1.DefaultNameSuffix)
87-
)
82+
// DefaultEKSControlPlaneRole is the name of the default IAM role to use for the EKS control plane
83+
// if no other role is supplied in the spec and if iam role creation is not enabled. The default
84+
// can be created using clusterawsadm or created manually.
85+
var DefaultEKSControlPlaneRole = fmt.Sprintf("eks-controlplane%s", iamv1.DefaultNameSuffix)
8886

8987
// IAMAuthenticatorConfig represents an aws-iam-authenticator configuration.
9088
type IAMAuthenticatorConfig struct {
@@ -279,3 +277,17 @@ type OIDCIdentityProviderConfig struct {
279277
// +optional
280278
Tags infrav1.Tags `json:"tags,omitempty"`
281279
}
280+
281+
// PodIdentityAssociation represents an association between a Kubernetes Service Account in a namespace, and an AWS IAM role which allows the service principal `pods.eks.amazonaws.com` in its trust policy.
282+
type PodIdentityAssociation struct {
283+
// ServiceAccountName is the name of the kubernetes Service Account within the namespace
284+
// +kubebuilder:validation:Required
285+
ServiceAccountName string `json:"serviceAccountName"`
286+
// ServiceAccountNamespace is the kubernetes namespace, which the kubernetes Service Account resides in. Defaults to "default" namespace.
287+
// +kubebuilder:validation:Required
288+
// +kubebuilder:default=default
289+
ServiceAccountNamespace string `json:"serviceAccountNamespace"`
290+
// RoleARN is the ARN of an IAM role which the Service Account can assume.
291+
// +kubebuilder:validation:Required
292+
RoleARN string `json:"serviceAccountRoleARN,omitempty"`
293+
}

controlplane/eks/api/v1beta2/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controlplane/eks/controllers/awsmanagedcontrolplane_controller_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ func mockedCallsForMissingEverything(ec2Rec *mocks.MockEC2APIMockRecorder, subne
409409
Name: aws.String("tag-key"),
410410
Values: aws.StringSlice([]string{"sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"}),
411411
},
412-
}})).Return(&ec2.DescribeRouteTablesOutput{
412+
},
413+
})).Return(&ec2.DescribeRouteTablesOutput{
413414
RouteTables: []*ec2.RouteTable{
414415
{
415416
Routes: []*ec2.Route{
@@ -469,7 +470,8 @@ func mockedCallsForMissingEverything(ec2Rec *mocks.MockEC2APIMockRecorder, subne
469470
Name: aws.String("state"),
470471
Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
471472
},
472-
}}), gomock.Any()).Return(nil).MinTimes(1).MaxTimes(2)
473+
},
474+
}), gomock.Any()).Return(nil).MinTimes(1).MaxTimes(2)
473475

474476
ec2Rec.DescribeAddressesWithContext(context.TODO(), gomock.Eq(&ec2.DescribeAddressesInput{
475477
Filters: []*ec2.Filter{
@@ -901,6 +903,12 @@ func mockedEKSCluster(g *WithT, eksRec *mock_eksiface.MockEKSAPIMockRecorder, ia
901903
ClusterName: aws.String("test-cluster"),
902904
}).Return(&eks.ListAddonsOutput{}, nil)
903905

906+
eksRec.ListPodIdentityAssociationsWithContext(context.TODO(), gomock.Eq(&eks.ListPodIdentityAssociationsInput{
907+
ClusterName: aws.String("test-cluster"),
908+
})).Return(&eks.ListPodIdentityAssociationsOutput{
909+
Associations: []*eks.PodIdentityAssociationSummary{},
910+
}, nil)
911+
904912
awsNodeRec.ReconcileCNI(gomock.Any()).Return(nil)
905913
kubeProxyRec.ReconcileKubeProxy(gomock.Any()).Return(nil)
906914
iamAuthenticatorRec.ReconcileIAMAuthenticator(gomock.Any()).Return(nil)

docs/book/src/SUMMARY_PREFIX.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- [Creating a cluster](./topics/eks/creating-a-cluster.md)
2020
- [Using EKS Console](./topics/eks/eks-console.md)
2121
- [Using EKS Addons](./topics/eks/addons.md)
22+
- [Using EKS Pod Identity Associations](./topics/eks/pod-identity-associations.md)
2223
- [Enabling Encryption](./topics/eks/encryption.md)
2324
- [Cluster Upgrades](./topics/eks/cluster-upgrades.md)
2425
- [ROSA Support](./topics/rosa/index.md)

0 commit comments

Comments
 (0)