diff --git a/cmd/clusterawsadm/api/bootstrap/v1beta1/defaults.go b/cmd/clusterawsadm/api/bootstrap/v1beta1/defaults.go index f4ed1f2ae5..3a810214ac 100644 --- a/cmd/clusterawsadm/api/bootstrap/v1beta1/defaults.go +++ b/cmd/clusterawsadm/api/bootstrap/v1beta1/defaults.go @@ -31,6 +31,8 @@ const ( DefaultStackName = "cluster-api-provider-aws-sigs-k8s-io" // DefaultPartitionName is the default security partition for AWS ARNs. DefaultPartitionName = "aws" + // DefaultPartitionNameUSGov is the default security partition for AWS ARNs. + DefaultPartitionNameUSGov = "us-gov" // DefaultKMSAliasPattern is the default KMS alias. DefaultKMSAliasPattern = "cluster-api-provider-aws-*" // DefaultS3BucketPrefix is the default S3 bucket prefix. diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/fargate.go b/cmd/clusterawsadm/cloudformation/bootstrap/fargate.go index 8ba8dfad6c..8c3d2ea261 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/fargate.go +++ b/cmd/clusterawsadm/cloudformation/bootstrap/fargate.go @@ -21,8 +21,13 @@ import ( "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/eks" ) -func fargateProfilePolicies(roleSpec *bootstrapv1.AWSIAMRoleSpec) []string { - policies := eks.FargateRolePolicies() +func (t Template) fargateProfilePolicies(roleSpec *bootstrapv1.AWSIAMRoleSpec) []string { + var policies []string + if t.Spec.Partition == bootstrapv1.DefaultPartitionNameUSGov { + policies = eks.FargateRolePoliciesAWSUSGov() + } else { + policies = eks.FargateRolePolicies() + } if roleSpec.ExtraPolicyAttachments != nil { policies = append(policies, roleSpec.ExtraPolicyAttachments...) } diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/managed_nodegroup.go b/cmd/clusterawsadm/cloudformation/bootstrap/managed_nodegroup.go index e96f4b50ac..f5a5431179 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/managed_nodegroup.go +++ b/cmd/clusterawsadm/cloudformation/bootstrap/managed_nodegroup.go @@ -16,10 +16,19 @@ limitations under the License. package bootstrap -import "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/eks" +import ( + "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/api/bootstrap/v1beta1" + "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/eks" +) func (t Template) eksMachinePoolPolicies() []string { - policies := eks.NodegroupRolePolicies() + + var policies []string + if t.Spec.Partition == v1beta1.DefaultPartitionNameUSGov { + policies = eks.NodegroupRolePoliciesAWSUSGov() + } else { + policies = eks.NodegroupRolePolicies() + } if t.Spec.EKS.ManagedMachinePool.ExtraPolicyAttachments != nil { policies = append(policies, t.Spec.EKS.ManagedMachinePool.ExtraPolicyAttachments...) } diff --git a/cmd/clusterawsadm/cloudformation/bootstrap/template.go b/cmd/clusterawsadm/cloudformation/bootstrap/template.go index 2ab20e87d4..ceecfe6904 100644 --- a/cmd/clusterawsadm/cloudformation/bootstrap/template.go +++ b/cmd/clusterawsadm/cloudformation/bootstrap/template.go @@ -200,7 +200,7 @@ func (t Template) RenderCloudFormation() *cloudformation.Template { template.Resources[AWSIAMRoleEKSFargate] = &cfn_iam.Role{ RoleName: expinfrav1.DefaultEKSFargateRole, AssumeRolePolicyDocument: AssumeRolePolicy(iamv1.PrincipalService, []string{eksiam.EKSFargateService}), - ManagedPolicyArns: fargateProfilePolicies(t.Spec.EKS.Fargate), + ManagedPolicyArns: t.fargateProfilePolicies(t.Spec.EKS.Fargate), Tags: converters.MapToCloudFormationTags(t.Spec.EKS.Fargate.Tags), } } diff --git a/pkg/cloud/services/ec2/ami.go b/pkg/cloud/services/ec2/ami.go index c9e0621069..760c7be025 100644 --- a/pkg/cloud/services/ec2/ami.go +++ b/pkg/cloud/services/ec2/ami.go @@ -44,6 +44,8 @@ const ( // https://ubuntu.com/server/docs/cloud-images/amazon-ec2 ubuntuOwnerID = "099720109477" + ubuntuOwnerIDUsGov = "513442679011" + // Description regex for fetching Ubuntu AMIs for bastion host. ubuntuImageDescription = "Canonical??Ubuntu??20.04?LTS??amd64?focal?image*" @@ -195,13 +197,9 @@ func GetLatestImage(imgs []*ec2.Image) (*ec2.Image, error) { return imgs[len(imgs)-1], nil } -func (s *Service) defaultBastionAMILookup() (string, error) { +func (s *Service) defaultBastionAMILookup(region string) (string, error) { describeImageInput := &ec2.DescribeImagesInput{ Filters: []*ec2.Filter{ - { - Name: aws.String("owner-id"), - Values: []*string{aws.String(ubuntuOwnerID)}, - }, { Name: aws.String("architecture"), Values: []*string{aws.String("x86_64")}, @@ -220,6 +218,21 @@ func (s *Service) defaultBastionAMILookup() (string, error) { }, }, } + + if strings.Contains(region, defaultUsGovPartitionName) { + filter := &ec2.Filter{ + Name: aws.String("owner-id"), + Values: []*string{aws.String(ubuntuOwnerIDUsGov)}, + } + describeImageInput.Filters = append(describeImageInput.Filters, filter) + } else { + filter := &ec2.Filter{ + Name: aws.String("owner-id"), + Values: []*string{aws.String(ubuntuOwnerID)}, + } + describeImageInput.Filters = append(describeImageInput.Filters, filter) + } + out, err := s.EC2Client.DescribeImages(describeImageInput) if err != nil { return "", errors.Wrapf(err, "failed to describe images within region: %q", s.scope.Region()) diff --git a/pkg/cloud/services/ec2/bastion.go b/pkg/cloud/services/ec2/bastion.go index ea9610962d..12989e33f4 100644 --- a/pkg/cloud/services/ec2/bastion.go +++ b/pkg/cloud/services/ec2/bastion.go @@ -36,6 +36,7 @@ import ( const ( defaultSSHKeyName = "default" + defaultUsGovPartitionName = "us-gov" ) var ( @@ -76,7 +77,7 @@ func (s *Service) ReconcileBastion() error { return errors.Wrap(err, "failed to patch conditions") } } - defaultBastion, err := s.getDefaultBastion(s.scope.Bastion().InstanceType, s.scope.Bastion().AMI) + defaultBastion, err := s.getDefaultBastion(s.scope.Bastion().InstanceType, s.scope.Bastion().AMI, instance) if err != nil { record.Warnf(s.scope.InfraCluster(), "FailedFetchingBastion", "Failed to fetch default bastion instance: %v", err) return err @@ -166,7 +167,7 @@ func (s *Service) describeBastionInstance() (*infrav1.Instance, error) { return nil, awserrors.NewNotFound("bastion host not found") } -func (s *Service) getDefaultBastion(instanceType, ami string) (*infrav1.Instance, error) { +func (s *Service) getDefaultBastion(instanceType, ami string, instance *infrav1.Instance) (*infrav1.Instance, error) { name := fmt.Sprintf("%s-bastion", s.scope.Name()) userData, _ := userdata.NewBastion(&userdata.BastionInput{}) @@ -186,9 +187,10 @@ func (s *Service) getDefaultBastion(instanceType, ami string) (*infrav1.Instance } } + region := s.scope.Region() if ami == "" { var err error - ami, err = s.defaultBastionAMILookup() + ami, err = s.defaultBastionAMILookup(region) if err != nil { return nil, err } diff --git a/pkg/cloud/services/eks/roles.go b/pkg/cloud/services/eks/roles.go index 91a73fe4b0..fe3e9e2897 100644 --- a/pkg/cloud/services/eks/roles.go +++ b/pkg/cloud/services/eks/roles.go @@ -18,6 +18,8 @@ package eks import ( "fmt" + "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/api/bootstrap/v1beta1" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" @@ -52,6 +54,22 @@ func FargateRolePolicies() []string { } } +// NodegroupRolePolicies gives the policies required for a nodegroup role. +func NodegroupRolePoliciesAWSUSGov() []string { + return []string{ + "arn:aws-us-gov:iam::aws:policy/AmazonEKSWorkerNodePolicy", + "arn:aws-us-gov:iam::aws:policy/AmazonEKS_CNI_Policy", //TODO: Can remove when CAPA supports provisioning of OIDC web identity federation with service account token volume projection + "arn:aws-us-gov:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", + } +} + +// FargateRolePolicies gives the policies required for a fargate role. +func FargateRolePoliciesAWSUSGov() []string { + return []string{ + "arn:aws-us-gov:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy", + } +} + func (s *Service) reconcileControlPlaneIAMRole() error { s.scope.V(2).Info("Reconciling EKS Control Plane IAM Role") @@ -94,7 +112,7 @@ func (s *Service) reconcileControlPlaneIAMRole() error { //TODO: check tags and trust relationship to see if they need updating policies := []*string{ - aws.String("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"), + aws.String("arn:*:iam::aws:policy/AmazonEKSClusterPolicy"), } if s.scope.ControlPlane.Spec.RoleAdditionalPolicies != nil { if !s.scope.AllowAdditionalRoles() && len(*s.scope.ControlPlane.Spec.RoleAdditionalPolicies) > 0 { @@ -203,7 +221,12 @@ func (s *NodegroupService) reconcileNodegroupIAMRole() error { return errors.Wrapf(err, "error ensuring tags and policy document are set on node role") } - policies := NodegroupRolePolicies() + var policies []string + if strings.Contains(s.scope.ControlPlane.Spec.Region, v1beta1.DefaultPartitionNameUSGov) { + policies = NodegroupRolePoliciesAWSUSGov() + } else { + policies = NodegroupRolePolicies() + } if len(s.scope.ManagedMachinePool.Spec.RoleAdditionalPolicies) > 0 { if !s.scope.AllowAdditionalRoles() { return ErrCannotUseAdditionalRoles @@ -319,7 +342,12 @@ func (s *FargateService) reconcileFargateIAMRole() (requeue bool, err error) { return updatedRole, errors.Wrapf(err, "error ensuring tags and policy document are set on fargate role") } - policies := FargateRolePolicies() + var policies []string + if strings.Contains(s.scope.ControlPlane.Spec.Region, v1beta1.DefaultPartitionNameUSGov) { + policies = FargateRolePoliciesAWSUSGov() + } else { + policies = FargateRolePolicies() + } updatedPolicies, err := s.EnsurePoliciesAttached(role, aws.StringSlice(policies)) if err != nil { return updatedRole, errors.Wrapf(err, "error ensuring policies are attached: %v", policies) diff --git a/pkg/cloud/services/iamauth/reconcile.go b/pkg/cloud/services/iamauth/reconcile.go index 7e2cd3e37a..3f53e74b4d 100644 --- a/pkg/cloud/services/iamauth/reconcile.go +++ b/pkg/cloud/services/iamauth/reconcile.go @@ -48,7 +48,7 @@ func (s *Service) ReconcileIAMAuthenticator(ctx context.Context) error { return fmt.Errorf("getting aws-iam-authenticator backend: %w", err) } - roleARN := fmt.Sprintf("arn:aws:iam::%s:role/nodes%s", accountID, iamv1.DefaultNameSuffix) + roleARN := fmt.Sprintf("arn:*:iam::%s:role/nodes%s", accountID, iamv1.DefaultNameSuffix) nodesRoleMapping := ekscontrolplanev1.RoleMapping{ RoleARN: roleARN, KubernetesMapping: ekscontrolplanev1.KubernetesMapping{ diff --git a/pkg/cloud/services/s3/s3.go b/pkg/cloud/services/s3/s3.go index e23609f66c..5f05169610 100644 --- a/pkg/cloud/services/s3/s3.go +++ b/pkg/cloud/services/s3/s3.go @@ -242,10 +242,10 @@ func (s *Service) bucketPolicy(bucketName string) (string, error) { Sid: "control-plane", Effect: iam.EffectAllow, Principal: map[iam.PrincipalType]iam.PrincipalID{ - iam.PrincipalAWS: []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", *accountID.Account, bucket.ControlPlaneIAMInstanceProfile)}, + iam.PrincipalAWS: []string{fmt.Sprintf("arn:*:iam::%s:role/%s", *accountID.Account, bucket.ControlPlaneIAMInstanceProfile)}, }, Action: []string{"s3:GetObject"}, - Resource: []string{fmt.Sprintf("arn:aws:s3:::%s/control-plane/*", bucketName)}, + Resource: []string{fmt.Sprintf("arn:*:s3:::%s/control-plane/*", bucketName)}, }, } @@ -254,10 +254,10 @@ func (s *Service) bucketPolicy(bucketName string) (string, error) { Sid: iamInstanceProfile, Effect: iam.EffectAllow, Principal: map[iam.PrincipalType]iam.PrincipalID{ - iam.PrincipalAWS: []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", *accountID.Account, iamInstanceProfile)}, + iam.PrincipalAWS: []string{fmt.Sprintf("arn:*:iam::%s:role/%s", *accountID.Account, iamInstanceProfile)}, }, Action: []string{"s3:GetObject"}, - Resource: []string{fmt.Sprintf("arn:aws:s3:::%s/node/*", bucketName)}, + Resource: []string{fmt.Sprintf("arn:*:s3:::%s/node/*", bucketName)}, }) }