Skip to content

Commit ed77fa0

Browse files
stefanmcshaneDanil-Grigorev
authored andcommitted
Add eks pod identities
1 parent 8d4c7f2 commit ed77fa0

18 files changed

+749
-9
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
@@ -3083,6 +3083,35 @@ spec:
30833083
description: Partition is the AWS security partition being used. Defaults
30843084
to "aws"
30853085
type: string
3086+
podIdentityAssociations:
3087+
description: |-
3088+
PodIdentityAssociations represent Kubernetes Service Accounts mapping to AWS IAM Roles without IRSA, using EKS Pod Identity.
3089+
This requires using the AWS EKS Addon for Pod Identity.
3090+
items:
3091+
description: PodIdentityAssociation represents an association between
3092+
a Kubernetes Service Account in a namespace, and an AWS IAM role
3093+
which allows the service principal `pods.eks.amazonaws.com` in
3094+
its trust policy.
3095+
properties:
3096+
serviceAccountName:
3097+
description: ServiceAccountName is the name of the kubernetes
3098+
Service Account within the namespace
3099+
type: string
3100+
serviceAccountNamespace:
3101+
default: default
3102+
description: ServiceAccountNamespace is the kubernetes namespace,
3103+
which the kubernetes Service Account resides in. Defaults
3104+
to "default" namespace.
3105+
type: string
3106+
serviceAccountRoleARN:
3107+
description: RoleARN is the ARN of an IAM role which the Service
3108+
Account can assume.
3109+
type: string
3110+
required:
3111+
- serviceAccountName
3112+
- serviceAccountNamespace
3113+
type: object
3114+
type: array
30863115
region:
30873116
description: The AWS Region the cluster lives in.
30883117
type: string

controlplane/eks/api/v1beta1/conversion.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ func (r *AWSManagedControlPlane) ConvertTo(dstRaw conversion.Hub) error {
4646
dst.Spec.RolePermissionsBoundary = restored.Spec.RolePermissionsBoundary
4747
dst.Status.Version = restored.Status.Version
4848
dst.Spec.BootstrapSelfManagedAddons = restored.Spec.BootstrapSelfManagedAddons
49+
dst.Spec.PodIdentityAssociations = restored.Spec.PodIdentityAssociations
50+
4951
return nil
5052
}
5153

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
@@ -188,6 +188,11 @@ type AWSManagedControlPlaneSpec struct { //nolint: maligned
188188
// +optional
189189
Addons *[]Addon `json:"addons,omitempty"`
190190

191+
// PodIdentityAssociations represent Kubernetes Service Accounts mapping to AWS IAM Roles without IRSA, using EKS Pod Identity.
192+
// This requires using the AWS EKS Addon for Pod Identity.
193+
// +optional
194+
PodIdentityAssociations []PodIdentityAssociation `json:"podIdentityAssociations,omitempty"`
195+
191196
// IdentityProviderconfig is used to specify the oidc provider config
192197
// to be attached with this eks cluster
193198
// +optional

controlplane/eks/api/v1beta2/awsmanagedcontrolplane_webhook.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/pkg/errors"
2626
apierrors "k8s.io/apimachinery/pkg/api/errors"
2727
"k8s.io/apimachinery/pkg/runtime"
28+
"k8s.io/apimachinery/pkg/util/validation"
2829
"k8s.io/apimachinery/pkg/util/validation/field"
2930
"k8s.io/apimachinery/pkg/util/version"
3031
"k8s.io/klog/v2"
@@ -107,6 +108,7 @@ func (*awsManagedControlPlaneWebhook) ValidateCreate(_ context.Context, obj runt
107108
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
108109
allErrs = append(allErrs, r.validateNetwork()...)
109110
allErrs = append(allErrs, r.validatePrivateDNSHostnameTypeOnLaunch()...)
111+
allErrs = append(allErrs, r.validServiceAccountName()...)
110112

111113
if len(allErrs) == 0 {
112114
return nil, nil
@@ -148,6 +150,7 @@ func (*awsManagedControlPlaneWebhook) ValidateUpdate(ctx context.Context, oldObj
148150
allErrs = append(allErrs, r.validateKubeProxy()...)
149151
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
150152
allErrs = append(allErrs, r.validatePrivateDNSHostnameTypeOnLaunch()...)
153+
allErrs = append(allErrs, r.validServiceAccountName()...)
151154

152155
if r.Spec.Region != oldAWSManagedControlplane.Spec.Region {
153156
allErrs = append(allErrs,
@@ -436,6 +439,28 @@ func (r *AWSManagedControlPlane) validatePrivateDNSHostnameTypeOnLaunch() field.
436439
return allErrs
437440
}
438441

442+
func (r *AWSManagedControlPlane) validServiceAccountName() field.ErrorList {
443+
var allErrs field.ErrorList
444+
445+
if r.Spec.PodIdentityAssociations != nil {
446+
for i, association := range r.Spec.PodIdentityAssociations {
447+
associationPath := field.NewPath("spec", "podIdentityAssociations").Index(i)
448+
if association.ServiceAccountName == "" {
449+
allErrs = append(allErrs, field.Required(associationPath.Child("serviceAccountName"), "serviceAccountName is required"))
450+
}
451+
452+
// kubernetes uses ValidateServiceAccountName internally, which maps to IsDNS1123Subdomain
453+
// https://github.com/kubernetes/apimachinery/blob/d794766488ac2892197a7cc8d0b4b46b0edbda80/pkg/api/validation/generic.go#L68
454+
455+
if validationErrs := validation.IsDNS1123Subdomain(association.ServiceAccountName); len(validationErrs) > 0 {
456+
allErrs = append(allErrs, field.Invalid(associationPath.Child("serviceAccountName"), association.ServiceAccountName, fmt.Sprintf("serviceAccountName is invalid: %v", validationErrs)))
457+
}
458+
}
459+
}
460+
461+
return allErrs
462+
}
463+
439464
func (r *AWSManagedControlPlane) validateNetwork() field.ErrorList {
440465
var allErrs field.ErrorList
441466

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: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,8 @@ func mockedCallsForMissingEverything(ec2Rec *mocks.MockEC2APIMockRecorder, subne
454454
Name: aws.String("tag-key"),
455455
Values: aws.StringSlice([]string{"sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"}),
456456
},
457-
}})).Return(&ec2.DescribeRouteTablesOutput{
457+
},
458+
})).Return(&ec2.DescribeRouteTablesOutput{
458459
RouteTables: []*ec2.RouteTable{
459460
{
460461
Routes: []*ec2.Route{
@@ -514,7 +515,8 @@ func mockedCallsForMissingEverything(ec2Rec *mocks.MockEC2APIMockRecorder, subne
514515
Name: aws.String("state"),
515516
Values: aws.StringSlice([]string{ec2.VpcStatePending, ec2.VpcStateAvailable}),
516517
},
517-
}}), gomock.Any()).Return(nil).MinTimes(1).MaxTimes(2)
518+
},
519+
}), gomock.Any()).Return(nil).MinTimes(1).MaxTimes(2)
518520

519521
ec2Rec.DescribeAddressesWithContext(context.TODO(), gomock.Eq(&ec2.DescribeAddressesInput{
520522
Filters: []*ec2.Filter{
@@ -949,6 +951,11 @@ func mockedEKSCluster(ctx context.Context, g *WithT, eksRec *mock_eksiface.MockE
949951
}).Return(&eks.ListAddonsOutput{}, nil)
950952

951953
eksRec.UpdateClusterConfig(ctx, gomock.AssignableToTypeOf(&eks.UpdateClusterConfigInput{})).After(waitUntilClusterActiveCall).Return(&eks.UpdateClusterConfigOutput{}, nil)
954+
eksRec.ListPodIdentityAssociationsWqithContext(context.TODO(), gomock.Eq(&eks.ListPodIdentityAssociationsInput{
955+
ClusterName: aws.String("test-cluster"),
956+
})).Return(&eks.ListPodIdentityAssociationsOutput{
957+
Associations: []*eks.PodIdentityAssociationSummary{},
958+
}, nil)
952959

953960
awsNodeRec.ReconcileCNI(gomock.Any()).Return(nil)
954961
kubeProxyRec.ReconcileKubeProxy(gomock.Any()).Return(nil)

docs/book/src/SUMMARY_PREFIX.md

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

0 commit comments

Comments
 (0)