Skip to content

WIP: ✨ feat: Support Access Entries #5583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ promote-images: $(KPROMO) $(YQ)

.PHONY: release-binaries
release-binaries: $(GORELEASER) ## Builds only the binaries, not a release.
$(GORELEASER) build --config $(GORELEASER_CONFIG) --snapshot --clean
GOMAXPROCS=2 $(GORELEASER) build --config $(GORELEASER_CONFIG) --snapshot --clean

.PHONY: release-staging
release-staging: ## Builds and push container images and manifests to the staging bucket.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2208,6 +2208,101 @@ spec:
description: AWSManagedControlPlaneSpec defines the desired state of an
Amazon EKS Cluster.
properties:
accessConfig:
description: AccessConfig specifies the access configuration information
for the cluster
properties:
accessEntries:
description: |-
AccessEntries specifies the access entries for the cluster
Access entries require AuthenticationMode to be either API or API_AND_CONFIG_MAP
items:
description: AccessEntry represents an AWS EKS access entry
for IAM principals
properties:
accessPolicies:
description: |-
AccessPolicies specifies the policies to associate with this access entry
Cannot be specified if Type is EC2_LINUX or EC2_WINDOWS
items:
description: AccessPolicyReference represents a reference
to an AWS EKS access policy
properties:
accessScope:
description: AccessScope specifies the scope for the
policy
properties:
namespaces:
description: |-
Namespaces are the namespaces for the access scope
Only valid when Type is namespace
items:
type: string
minItems: 1
type: array
type:
default: cluster
description: Type is the type of access scope.
Defaults to "cluster".
enum:
- cluster
- namespace
type: string
required:
- type
type: object
policyARN:
description: PolicyARN is the Amazon Resource Name
(ARN) of the access policy
type: string
required:
- accessScope
- policyARN
type: object
maxItems: 20
type: array
kubernetesGroups:
description: |-
KubernetesGroups represents the Kubernetes groups for the access entry
Cannot be specified if Type is EC2_LINUX or EC2_WINDOWS
items:
type: string
type: array
principalARN:
description: PrincipalARN is the Amazon Resource Name (ARN)
of the IAM principal
type: string
type:
default: STANDARD
description: Type is the type of access entry. Defaults
to STANDARD if not specified.
enum:
- STANDARD
- EC2_LINUX
- EC2_WINDOWS
- FARGATE_LINUX
- EC2
- HYBRID_LINUX
- HYPERPOD_LINUX
type: string
username:
description: Username is the username for the access entry
type: string
required:
- principalARN
type: object
type: array
authenticationMode:
default: CONFIG_MAP
description: |-
AuthenticationMode specifies the desired authentication mode for the cluster
Defaults to CONFIG_MAP
enum:
- CONFIG_MAP
- API
- API_AND_CONFIG_MAP
type: string
type: object
additionalTags:
additionalProperties:
type: string
Expand Down
1 change: 1 addition & 0 deletions controlplane/eks/api/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (r *AWSManagedControlPlane) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.VpcCni.Disable = r.Spec.DisableVPCCNI
dst.Spec.Partition = restored.Spec.Partition
dst.Spec.RestrictPrivateSubnets = restored.Spec.RestrictPrivateSubnets
dst.Spec.AccessConfig = restored.Spec.AccessConfig
dst.Spec.RolePath = restored.Spec.RolePath
dst.Spec.RolePermissionsBoundary = restored.Spec.RolePermissionsBoundary
dst.Status.Version = restored.Status.Version
Expand Down
1 change: 1 addition & 0 deletions controlplane/eks/api/v1beta1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ type AWSManagedControlPlaneSpec struct { //nolint: maligned
// +optional
OIDCIdentityProviderConfig *OIDCIdentityProviderConfig `json:"oidcIdentityProviderConfig,omitempty"`

// AccessConfig specifies the access configuration information for the cluster
// +optional
AccessConfig *AccessConfig `json:"accessConfig,omitempty"`

// VpcCni is used to set configuration options for the VPC CNI plugin
// +optional
VpcCni VpcCni `json:"vpcCni,omitempty"`
Expand Down Expand Up @@ -249,6 +253,73 @@ type EndpointAccess struct {
Private *bool `json:"private,omitempty"`
}

// AccessEntry represents an AWS EKS access entry for IAM principals
type AccessEntry struct {
// PrincipalARN is the Amazon Resource Name (ARN) of the IAM principal
// +kubebuilder:validation:Required
PrincipalARN string `json:"principalARN"`

// Type is the type of access entry. Defaults to STANDARD if not specified.
// +kubebuilder:default=STANDARD
// +kubebuilder:validation:Enum=STANDARD;EC2_LINUX;EC2_WINDOWS;FARGATE_LINUX;EC2;HYBRID_LINUX;HYPERPOD_LINUX
// +optional
Type string `json:"type,omitempty"`

// KubernetesGroups represents the Kubernetes groups for the access entry
// Cannot be specified if Type is EC2_LINUX or EC2_WINDOWS
// +optional
KubernetesGroups []string `json:"kubernetesGroups,omitempty"`

// Username is the username for the access entry
// +optional
Username string `json:"username,omitempty"`

// AccessPolicies specifies the policies to associate with this access entry
// Cannot be specified if Type is EC2_LINUX or EC2_WINDOWS
// +optional
// +kubebuilder:validation:MaxItems=20
AccessPolicies []AccessPolicyReference `json:"accessPolicies,omitempty"`
}

// AccessPolicyReference represents a reference to an AWS EKS access policy
type AccessPolicyReference struct {
// PolicyARN is the Amazon Resource Name (ARN) of the access policy
// +kubebuilder:validation:Required
PolicyARN string `json:"policyARN"`

// AccessScope specifies the scope for the policy
// +kubebuilder:validation:Required
AccessScope AccessScope `json:"accessScope"`
}

// AccessScope represents the scope for an access policy
type AccessScope struct {
// Type is the type of access scope. Defaults to "cluster".
// +kubebuilder:validation:Enum=cluster;namespace
// +kubebuilder:default=cluster
Type string `json:"type"`

// Namespaces are the namespaces for the access scope
// Only valid when Type is namespace
// +optional
// +kubebuilder:validation:MinItems=1
Namespaces []string `json:"namespaces,omitempty"`
}

// AccessConfig represents the access configuration information for the cluster
type AccessConfig struct {
// AuthenticationMode specifies the desired authentication mode for the cluster
// Defaults to CONFIG_MAP
// +kubebuilder:default=CONFIG_MAP
// +kubebuilder:validation:Enum=CONFIG_MAP;API;API_AND_CONFIG_MAP
AuthenticationMode EKSAuthenticationMode `json:"authenticationMode,omitempty"`

// AccessEntries specifies the access entries for the cluster
// Access entries require AuthenticationMode to be either API or API_AND_CONFIG_MAP
// +optional
AccessEntries []AccessEntry `json:"accessEntries,omitempty"`
}

// EncryptionConfig specifies the encryption configuration for the EKS clsuter.
type EncryptionConfig struct {
// Provider specifies the ARN or alias of the CMK (in AWS KMS)
Expand Down
75 changes: 75 additions & 0 deletions controlplane/eks/api/v1beta2/awsmanagedcontrolplane_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func (*awsManagedControlPlaneWebhook) ValidateCreate(_ context.Context, obj runt
// TODO: Add ipv6 validation things in these validations.
allErrs = append(allErrs, r.validateEKSVersion(nil)...)
allErrs = append(allErrs, r.Spec.Bastion.Validate()...)
allErrs = append(allErrs, r.validateAccessConfig(nil)...)
allErrs = append(allErrs, r.validateIAMAuthConfig()...)
allErrs = append(allErrs, r.validateSecondaryCIDR()...)
allErrs = append(allErrs, r.validateEKSAddons()...)
Expand Down Expand Up @@ -140,6 +141,7 @@ func (*awsManagedControlPlaneWebhook) ValidateUpdate(ctx context.Context, oldObj
allErrs = append(allErrs, r.validateEKSClusterNameSame(oldAWSManagedControlplane)...)
allErrs = append(allErrs, r.validateEKSVersion(oldAWSManagedControlplane)...)
allErrs = append(allErrs, r.Spec.Bastion.Validate()...)
allErrs = append(allErrs, r.validateAccessConfig(oldAWSManagedControlplane)...)
allErrs = append(allErrs, r.validateIAMAuthConfig()...)
allErrs = append(allErrs, r.validateSecondaryCIDR()...)
allErrs = append(allErrs, r.validateEKSAddons()...)
Expand Down Expand Up @@ -304,6 +306,79 @@ func (r *AWSManagedControlPlane) validateEKSAddons() field.ErrorList {
return allErrs
}

func (r *AWSManagedControlPlane) validateAccessConfig(old *AWSManagedControlPlane) field.ErrorList {
var allErrs field.ErrorList

// If accessConfig is already set, do not allow removal of it.
if old != nil && old.Spec.AccessConfig != nil && r.Spec.AccessConfig == nil {
allErrs = append(allErrs,
field.Invalid(field.NewPath("spec", "accessConfig"), r.Spec.AccessConfig, "removing AccessConfig is not allowed after it has been enabled"),
)
}

// AuthenticationMode is ratcheting - do not allow downgrades
if old != nil && old.Spec.AccessConfig != nil && r.Spec.AccessConfig != nil && old.Spec.AccessConfig.AuthenticationMode != r.Spec.AccessConfig.AuthenticationMode &&
((old.Spec.AccessConfig.AuthenticationMode == EKSAuthenticationModeAPIAndConfigMap && r.Spec.AccessConfig.AuthenticationMode == EKSAuthenticationModeConfigMap) ||
old.Spec.AccessConfig.AuthenticationMode == EKSAuthenticationModeAPI) {
allErrs = append(allErrs,
field.Invalid(field.NewPath("spec", "accessConfig", "authenticationMode"), r.Spec.AccessConfig.AuthenticationMode, "downgrading authentication mode is not allowed after it has been enabled"),
)
}

// AccessEntries require AuthenticationMode to be API or API_AND_CONFIG_MAP
if r.Spec.AccessConfig != nil && len(r.Spec.AccessConfig.AccessEntries) > 0 {
if r.Spec.AccessConfig.AuthenticationMode != EKSAuthenticationModeAPI &&
r.Spec.AccessConfig.AuthenticationMode != EKSAuthenticationModeAPIAndConfigMap {
allErrs = append(allErrs,
field.Invalid(
field.NewPath("spec", "accessConfig", "accessEntries"),
r.Spec.AccessConfig.AccessEntries,
"accessEntries can only be used when authenticationMode is set to API or API_AND_CONFIG_MAP",
),
)
}

// Validate that EC2 types don't have kubernetes groups or access policies
for i, entry := range r.Spec.AccessConfig.AccessEntries {
if entry.Type == "EC2_LINUX" || entry.Type == "EC2_WINDOWS" {
if len(entry.KubernetesGroups) > 0 {
allErrs = append(allErrs,
field.Invalid(
field.NewPath("spec", "accessConfig", "accessEntries").Index(i).Child("kubernetesGroups"),
entry.KubernetesGroups,
"kubernetesGroups cannot be specified when type is EC2_LINUX or EC2_WINDOWS",
),
)
}
if len(entry.AccessPolicies) > 0 {
allErrs = append(allErrs,
field.Invalid(
field.NewPath("spec", "accessConfig", "accessEntries").Index(i).Child("accessPolicies"),
entry.AccessPolicies,
"accessPolicies cannot be specified when type is EC2_LINUX or EC2_WINDOWS",
),
)
}
}

// Validate namespace scopes
for j, policy := range entry.AccessPolicies {
if policy.AccessScope.Type == "namespace" && len(policy.AccessScope.Namespaces) == 0 {
allErrs = append(allErrs,
field.Invalid(
field.NewPath("spec", "accessConfig", "accessEntries").Index(i).Child("accessPolicies").Index(j).Child("accessScope", "namespaces"),
policy.AccessScope.Namespaces,
"at least one value must be provided when accessScope type is namespace",
),
)
}
}
}
}

return allErrs
}

func (r *AWSManagedControlPlane) validateIAMAuthConfig() field.ErrorList {
var allErrs field.ErrorList

Expand Down
Loading