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
7 changes: 7 additions & 0 deletions api/v1beta1/awscluster_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1beta1

import (
apiconversion "k8s.io/apimachinery/pkg/conversion"
"sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
infrav2 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
"sigs.k8s.io/controller-runtime/pkg/conversion"
Expand Down Expand Up @@ -64,6 +65,8 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
dst.Status.Bastion.MarketType = restored.Status.Bastion.MarketType
}
dst.Spec.Partition = restored.Spec.Partition
dst.Spec.AssociateOIDCProvider = restored.Spec.AssociateOIDCProvider
dst.Status.OIDCProvider = restored.Status.OIDCProvider

for role, sg := range restored.Status.Network.SecurityGroups {
dst.Status.Network.SecurityGroups[role] = sg
Expand Down Expand Up @@ -222,3 +225,7 @@ func (r *AWSClusterList) ConvertFrom(srcRaw conversion.Hub) error {
func Convert_v1beta2_SubnetSpec_To_v1beta1_SubnetSpec(in *infrav2.SubnetSpec, out *SubnetSpec, s apiconversion.Scope) error {
return autoConvert_v1beta2_SubnetSpec_To_v1beta1_SubnetSpec(in, out, s)
}

func Convert_v1beta2_AWSClusterStatus_To_v1beta1_AWSClusterStatus(in *v1beta2.AWSClusterStatus, out *AWSClusterStatus, scope apiconversion.Scope) error {
return autoConvert_v1beta2_AWSClusterStatus_To_v1beta1_AWSClusterStatus(in, out, scope)
}
8 changes: 0 additions & 8 deletions api/v1beta1/s3bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import (
"net"

"k8s.io/apimachinery/pkg/util/validation/field"

"sigs.k8s.io/cluster-api-provider-aws/v2/feature"
)

// Validate validates S3Bucket fields.
Expand All @@ -37,12 +35,6 @@ func (b *S3Bucket) Validate() []*field.Error {
errs = append(errs, field.Required(field.NewPath("spec", "s3Bucket", "name"), "can't be empty"))
}

// Feature gate is not enabled but ignition is enabled then send a forbidden error.
if !feature.Gates.Enabled(feature.BootstrapFormatIgnition) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this removed on purpose?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, this PR was based on work started by @luthermonson so I don't have the full history.
I see the check still exists in the v1beta2 validation, so I'll revert this and do some tests.

errs = append(errs, field.Forbidden(field.NewPath("spec", "s3Bucket"),
"can be set only if the BootstrapFormatIgnition feature gate is enabled"))
}

if b.ControlPlaneIAMInstanceProfile == "" {
errs = append(errs,
field.Required(field.NewPath("spec", "s3Bucket", "controlPlaneIAMInstanceProfiles"), "can't be empty"))
Expand Down
17 changes: 7 additions & 10 deletions api/v1beta1/zz_generated.conversion.go

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

23 changes: 23 additions & 0 deletions api/v1beta2/awscluster_spec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package v1beta2

import (
"k8s.io/apimachinery/pkg/util/validation/field"

"sigs.k8s.io/cluster-api-provider-aws/v2/feature"
)

// Validate will validate the spec fields.
func (s *AWSClusterSpec) Validate() []*field.Error {
var errs field.ErrorList

// Check the feature gate is enabled for OIDC Provider.
if s.AssociateOIDCProvider && !feature.Gates.Enabled(feature.OIDCProviderUnmanagedClusters) {
errs = append(errs,
field.Forbidden(field.NewPath("spec", "associateOIDCProvider"),
"can be enabled only if the OIDCProviderUnmanagedClusters feature gate is enabled"),
)
return errs
}

return errs
}
16 changes: 13 additions & 3 deletions api/v1beta2/awscluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,17 @@ type AWSClusterSpec struct {
IdentityRef *AWSIdentityReference `json:"identityRef,omitempty"`

// S3Bucket contains options to configure a supporting S3 bucket for this
// cluster - currently used for nodes requiring Ignition
// (https://coreos.github.io/ignition/) for bootstrapping (requires
// BootstrapFormatIgnition feature flag to be enabled).
// cluster - Used for nodes requiring Ignition (https://coreos.github.io/ignition/) for bootstrapping (requires
// BootstrapFormatIgnition feature flag to be enabled) and/or for storing OIDC endpoint certificates for use
// with IRSA (requires OIDCProviderUnmanagedClusters feature flag to be enabled).
// +optional
S3Bucket *S3Bucket `json:"s3Bucket,omitempty"`

// AssociateOIDCProvider can be enabled to automatically publish the clusters Service Account issuer OIDC discovery
// documents to S3, create an AWS IAM OIDC identity provider and configure it to trust the cluster issuer.
// This will only work if the S3Bucket is configured properly.
// +kubebuilder:default=false
AssociateOIDCProvider bool `json:"associateOIDCProvider,omitempty"`
}

// AWSIdentityKind defines allowed AWS identity types.
Expand Down Expand Up @@ -281,6 +287,10 @@ type AWSClusterStatus struct {
FailureDomains clusterv1.FailureDomains `json:"failureDomains,omitempty"`
Bastion *Instance `json:"bastion,omitempty"`
Conditions clusterv1.Conditions `json:"conditions,omitempty"`

// OIDCProvider holds the status of the identity provider for this cluster
// +optional
OIDCProvider *OIDCProviderStatus `json:"oidcProvider,omitempty"`
}

// S3Bucket defines a supporting S3 bucket for the cluster, currently can be optionally used for Ignition.
Expand Down
1 change: 1 addition & 0 deletions api/v1beta2/awscluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func (r *AWSCluster) ValidateCreate() (admission.Warnings, error) {
var allErrs field.ErrorList
var allWarnings admission.Warnings

allErrs = append(allErrs, r.Spec.Validate()...)
allErrs = append(allErrs, r.Spec.Bastion.Validate()...)
allErrs = append(allErrs, r.validateSSHKeyName()...)
allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
Expand Down
8 changes: 8 additions & 0 deletions api/v1beta2/conditions_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,11 @@ const (
// S3BucketFailedReason is used when any errors occur during reconciliation of an S3 bucket.
S3BucketFailedReason = "S3BucketCreationFailed"
)

const (
// OIDCProviderReadyCondition indicates that the OIDC provider has been created successfully.
OIDCProviderReadyCondition = "OIDCProviderCreated"

// OIDCProviderReconciliationFailedReason is used if we can't reconcile the OIDC provider.
OIDCProviderReconciliationFailedReason = "OIDCProviderReconciliationFailed"
)
7 changes: 4 additions & 3 deletions api/v1beta2/s3bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ func (b *S3Bucket) Validate() []*field.Error {
errs = append(errs, field.Required(field.NewPath("spec", "s3Bucket", "name"), "can't be empty"))
}

// Feature gate is not enabled but ignition is enabled then send a forbidden error.
if !feature.Gates.Enabled(feature.BootstrapFormatIgnition) {
// Either the BootstrapFormatIgnition or OIDCProviderUnmanagedClusters feature gate must be enabled.
// Otherwise send a forbidden error.
if !feature.Gates.Enabled(feature.BootstrapFormatIgnition) && !feature.Gates.Enabled(feature.OIDCProviderUnmanagedClusters) {
errs = append(errs, field.Forbidden(field.NewPath("spec", "s3Bucket"),
"can be set only if the BootstrapFormatIgnition feature gate is enabled"))
"can be set only if the BootstrapFormatIgnition or OIDCProviderUnmanagedClusters feature gate is enabled"))
}

if b.PresignedURLDuration == nil {
Expand Down
10 changes: 10 additions & 0 deletions api/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,16 @@ const (
AmazonLinuxGPU EKSAMILookupType = "AmazonLinuxGPU"
)

// OIDCProviderStatus holds the status of the AWS OIDC identity provider.
type OIDCProviderStatus struct {
// ARN holds the ARN of the provider
ARN string `json:"arn,omitempty"`
// IssuerURL holds the OIDC Issuer URL of the cluster.
IssuerURL string `json:"issuerUrl,omitempty"`
// TrustPolicy contains the boilerplate IAM trust policy to use for IRSA
TrustPolicy string `json:"trustPolicy,omitempty"`
}

// PrivateDNSName is the options for the instance hostname.
type PrivateDNSName struct {
// EnableResourceNameDNSAAAARecord indicates whether to respond to DNS queries for instance hostnames with DNS AAAA records.
Expand Down
20 changes: 20 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

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

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

5 changes: 5 additions & 0 deletions cmd/clusterawsadm/api/bootstrap/v1beta1/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ func SetDefaults_AWSIAMConfigurationSpec(obj *AWSIAMConfigurationSpec) { //nolin
if obj.S3Buckets.NamePrefix == "" {
obj.S3Buckets.NamePrefix = DefaultS3BucketPrefix
}
if obj.IAMIdentityProviders == nil {
obj.IAMIdentityProviders = &IAMIdentityProviders{
Enable: false,
}
}
}

// SetDefaults_AWSIAMConfiguration is used by defaulter-gen.
Expand Down
12 changes: 12 additions & 0 deletions cmd/clusterawsadm/api/bootstrap/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ type S3Buckets struct {
NamePrefix string `json:"namePrefix"`
}

// IAMIdentityProviders controls the configuration of the AWS IAM role for IAM
// Identity providers, which can be created to support IRSA for unmanaged clusters.
type IAMIdentityProviders struct {
// Enable controls whether permissions are granted to manage IAM Identity Providers
Enable bool `json:"enable,omitempty"`
}

// AWSIAMConfigurationSpec defines the specification of the AWSIAMConfiguration.
type AWSIAMConfigurationSpec struct {
// NamePrefix will be prepended to every AWS IAM role, user and policy created by clusterawsadm. Defaults to "".
Expand Down Expand Up @@ -235,6 +242,11 @@ type AWSIAMConfigurationSpec struct {

// AllowAssumeRole enables the sts:AssumeRole permission within the CAPA policies
AllowAssumeRole bool `json:"allowAssumeRole,omitempty"`

// IAMIdentityProviders, when enabled, will add controller nodes permissions to
// create IAM Identity Providers for kubeadm workload clusters.
// +optional
IAMIdentityProviders *IAMIdentityProviders `json:"iamIdentityProviders,omitempty"`
}

// GetObjectKind returns the AAWSIAMConfiguration's TypeMeta.
Expand Down
20 changes: 20 additions & 0 deletions cmd/clusterawsadm/api/bootstrap/v1beta1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,19 @@ func (t Template) ControllersPolicy() *iamv1.PolicyDocument {
},
})
}
if t.Spec.IAMIdentityProviders.Enable {
statement = append(statement, iamv1.StatementEntry{
Effect: iamv1.EffectAllow,
Resource: iamv1.Resources{
fmt.Sprintf("arn:*:s3:::%s*", t.Spec.S3Buckets.NamePrefix),
},
Action: iamv1.Actions{
"s3:PutBucketOwnershipControls",
"s3:PutObjectAcl",
"s3:PutBucketPublicAccessBlock",
},
})
}
if t.Spec.EventBridge.Enable {
statement = append(statement, iamv1.StatementEntry{
Effect: iamv1.EffectAllow,
Expand All @@ -323,6 +336,19 @@ func (t Template) ControllersPolicy() *iamv1.PolicyDocument {
},
})
}
if t.Spec.IAMIdentityProviders.Enable {
statement = append(statement, iamv1.StatementEntry{
Effect: iamv1.EffectAllow,
Resource: iamv1.Resources{iamv1.Any},
Action: iamv1.Actions{
"iam:CreateOpenIDConnectProvider",
"iam:DeleteOpenIDConnectProvider",
"iam:ListOpenIDConnectProviders",
"iam:GetOpenIDConnectProvider",
"iam:TagOpenIDConnectProvider",
},
})
}

return &iamv1.PolicyDocument{
Version: iamv1.CurrentVersion,
Expand Down
Loading
Loading