Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -2245,6 +2245,28 @@ 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:
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
bootstrapClusterCreatorAdminPermissions:
default: true
description: |-
BootstrapClusterCreatorAdminPermissions grants cluster admin permissions
to the IAM identity creating the cluster. Only applied during creation,
ignored when updating existing clusters. Defaults to true.
type: boolean
type: object
additionalTags:
additionalProperties:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ 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:
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
bootstrapClusterCreatorAdminPermissions:
default: true
description: |-
BootstrapClusterCreatorAdminPermissions grants cluster admin permissions
to the IAM identity creating the cluster. Only applied during creation,
ignored when updating existing clusters. Defaults to true.
type: boolean
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 @@ -117,6 +117,7 @@ func (r *AWSManagedControlPlane) ConvertTo(dstRaw conversion.Hub) error {

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.

19 changes: 19 additions & 0 deletions controlplane/eks/api/v1beta2/awsmanagedcontrolplane_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,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 @@ -248,6 +252,21 @@ type EndpointAccess struct {
Private *bool `json:"private,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"`

// BootstrapClusterCreatorAdminPermissions grants cluster admin permissions
// to the IAM identity creating the cluster. Only applied during creation,
// ignored when updating existing clusters. Defaults to true.
// +kubebuilder:default=true
BootstrapClusterCreatorAdminPermissions *bool `json:"bootstrapClusterCreatorAdminPermissions,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
49 changes: 49 additions & 0 deletions controlplane/eks/api/v1beta2/awsmanagedcontrolplane_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func (*awsManagedControlPlaneWebhook) ValidateCreate(_ context.Context, obj runt
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
allErrs = append(allErrs, r.validateNetwork()...)
allErrs = append(allErrs, r.validatePrivateDNSHostnameTypeOnLaunch()...)
allErrs = append(allErrs, r.validateAccessConfigCreate()...)

if len(allErrs) == 0 {
return nil, nil
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.validateAccessConfigUpdate(oldAWSManagedControlplane)...)
allErrs = append(allErrs, r.validateIAMAuthConfig()...)
allErrs = append(allErrs, r.validateSecondaryCIDR()...)
allErrs = append(allErrs, r.validateEKSAddons()...)
Expand Down Expand Up @@ -318,6 +320,53 @@ func validateEKSAddons(eksVersion *string, networkSpec infrav1.NetworkSpec, addo
return allErrs
}

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

// If accessConfig is already set, do not allow removal of it.
if 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.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"),
)
}

// BootstrapClusterCreatorAdminPermissions only applies on create, but changes should not invalidate updates
if old.Spec.AccessConfig != nil && r.Spec.AccessConfig != nil &&
old.Spec.AccessConfig.BootstrapClusterCreatorAdminPermissions != r.Spec.AccessConfig.BootstrapClusterCreatorAdminPermissions {
mcpLog.Info("Ignoring changes to BootstrapClusterCreatorAdminPermissions on cluster update", "old", old.Spec.AccessConfig.BootstrapClusterCreatorAdminPermissions, "new", r.Spec.AccessConfig.BootstrapClusterCreatorAdminPermissions)
}

return allErrs
}

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

if r.Spec.AccessConfig != nil {
if r.Spec.AccessConfig.AuthenticationMode == EKSAuthenticationModeConfigMap &&
r.Spec.AccessConfig.BootstrapClusterCreatorAdminPermissions != nil &&
!*r.Spec.AccessConfig.BootstrapClusterCreatorAdminPermissions {
allErrs = append(allErrs,
field.Invalid(field.NewPath("spec", "accessConfig", "bootstrapClusterCreatorAdminPermissions"),
*r.Spec.AccessConfig.BootstrapClusterCreatorAdminPermissions,
"bootstrapClusterCreatorAdminPermissions must be true if cluster authentication mode is set to config_map"),
)
}
}

return allErrs
}

func (r *AWSManagedControlPlane) validateIAMAuthConfig() field.ErrorList {
return validateIAMAuthConfig(r.Spec.IAMAuthenticatorConfig, field.NewPath("spec.iamAuthenticatorConfig"))
}
Expand Down
151 changes: 151 additions & 0 deletions controlplane/eks/api/v1beta2/awsmanagedcontrolplane_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func TestWebhookCreate(t *testing.T) {
secondaryCidr *string
secondaryCidrBlocks []infrav1.VpcCidrBlock
kubeProxy KubeProxy
accessConfig *AccessConfig
}{
{
name: "ekscluster specified",
Expand Down Expand Up @@ -322,6 +323,47 @@ func TestWebhookCreate(t *testing.T) {
Disable: true,
},
},
{
name: "BootstrapClusterCreatorAdminPermissions true with EKSAuthenticationModeConfigMap",
eksClusterName: "default_cluster1",
eksVersion: "v1.19",
expectError: false,
accessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
BootstrapClusterCreatorAdminPermissions: ptr.To(true),
},
},
{
name: "BootstrapClusterCreatorAdminPermissions false with EKSAuthenticationModeConfigMap",
eksClusterName: "default_cluster1",
eksVersion: "v1.19",
expectError: true,
expectErrorToContain: "bootstrapClusterCreatorAdminPermissions must be true if cluster authentication mode is set to config_map",
accessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
BootstrapClusterCreatorAdminPermissions: ptr.To(false),
},
},
{
name: "BootstrapClusterCreatorAdminPermissions false with EKSAuthenticationModeAPIAndConfigMap",
eksClusterName: "default_cluster1",
eksVersion: "v1.19",
expectError: false,
accessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeAPIAndConfigMap,
BootstrapClusterCreatorAdminPermissions: ptr.To(false),
},
},
{
name: "BootstrapClusterCreatorAdminPermissions false with EKSAuthenticationModeAPI",
eksClusterName: "default_cluster1",
eksVersion: "v1.19",
expectError: false,
accessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeAPI,
BootstrapClusterCreatorAdminPermissions: ptr.To(false),
},
},
}

for _, tc := range tests {
Expand Down Expand Up @@ -365,6 +407,9 @@ func TestWebhookCreate(t *testing.T) {
if tc.secondaryCidr != nil {
mcp.Spec.SecondaryCidrBlock = tc.secondaryCidr
}
if tc.accessConfig != nil {
mcp.Spec.AccessConfig = tc.accessConfig
}

err := testEnv.Create(ctx, mcp)

Expand Down Expand Up @@ -603,6 +648,112 @@ func TestWebhookUpdate(t *testing.T) {
},
expectError: false,
},
{
name: "no change in access config",
oldClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
},
},
newClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
},
},
expectError: false,
},
{
name: "change in access config to nil",
oldClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
},
},
newClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
},
expectError: true,
},
{
name: "change in access config from nil to valid",
oldClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
},
newClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
},
},
expectError: false,
},
{
name: "change in access config auth mode from ApiAndConfigMap to API is allowed",
oldClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeAPIAndConfigMap,
},
},
newClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeAPI,
},
},
expectError: false,
},
{
name: "change in access config auth mode from API to Config Map is denied",
oldClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeAPI,
},
},
newClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
},
},
expectError: true,
},
{
name: "change in access config auth mode from APIAndConfigMap to Config Map is denied",
oldClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeAPIAndConfigMap,
},
},
newClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
AuthenticationMode: EKSAuthenticationModeConfigMap,
},
},
expectError: true,
},
{
name: "change in access config bootstrap admin permissions is ignored",
oldClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
BootstrapClusterCreatorAdminPermissions: ptr.To(true),
},
},
newClusterSpec: AWSManagedControlPlaneSpec{
EKSClusterName: "default_cluster1",
AccessConfig: &AccessConfig{
BootstrapClusterCreatorAdminPermissions: ptr.To(false),
},
},
expectError: false,
},
{
name: "change in encryption config to nil",
oldClusterSpec: AWSManagedControlPlaneSpec{
Expand Down
21 changes: 21 additions & 0 deletions controlplane/eks/api/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1beta2

import (
"fmt"
"strings"

ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -79,6 +80,26 @@ var (
EKSTokenMethodAWSCli = EKSTokenMethod("aws-cli")
)

// EKSAuthenticationMode defines the authentication mode for the cluster
type EKSAuthenticationMode string

// APIValue returns the corresponding EKS API value for the authentication mode
func (e EKSAuthenticationMode) APIValue() ekstypes.AuthenticationMode {
return ekstypes.AuthenticationMode(strings.ToUpper(string(e)))
}

var (
// EKSAuthenticationModeConfigMap indicates that only `aws-auth` ConfigMap will be used for authentication
EKSAuthenticationModeConfigMap = EKSAuthenticationMode("config_map")

// EKSAuthenticationModeAPI indicates that only AWS Access Entries will be used for authentication
EKSAuthenticationModeAPI = EKSAuthenticationMode("api")

// EKSAuthenticationModeAPIAndConfigMap indicates that both `aws-auth` ConfigMap and AWS Access Entries will
// be used for authentication
EKSAuthenticationModeAPIAndConfigMap = EKSAuthenticationMode("api_and_config_map")
)

var (
// DefaultEKSControlPlaneRole is the name of the default IAM role to use for the EKS control plane
// if no other role is supplied in the spec and if iam role creation is not enabled. The default
Expand Down
Loading