sigs.k8s.io/cluster-api-provider-aws@v1.5.5/cmd/clusterawsadm/cloudformation/bootstrap/cluster_api_controller.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package bootstrap
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/awslabs/goformation/v4/cloudformation"
    23  	cfn_iam "github.com/awslabs/goformation/v4/cloudformation/iam"
    24  
    25  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    26  	iamv1 "sigs.k8s.io/cluster-api-provider-aws/iam/api/v1beta1"
    27  )
    28  
    29  const (
    30  	eksClusterPolicyName = "AmazonEKSClusterPolicy"
    31  )
    32  
    33  func (t Template) controllersPolicyGroups() []string {
    34  	groups := []string{}
    35  	if t.Spec.BootstrapUser.Enable {
    36  		groups = append(groups, cloudformation.Ref(AWSIAMGroupBootstrapper))
    37  	}
    38  	return groups
    39  }
    40  
    41  func (t Template) controllersPolicyRoleAttachments() []string {
    42  	attachments := []string{
    43  		cloudformation.Ref(AWSIAMRoleControllers),
    44  	}
    45  	if !t.Spec.ControlPlane.DisableClusterAPIControllerPolicyAttachment {
    46  		attachments = append(
    47  			attachments,
    48  			cloudformation.Ref(AWSIAMRoleControlPlane),
    49  		)
    50  	}
    51  	return attachments
    52  }
    53  
    54  func (t Template) controllersTrustPolicy() *iamv1.PolicyDocument {
    55  	policyDocument := ec2AssumeRolePolicy()
    56  	policyDocument.Statement = append(policyDocument.Statement, t.Spec.ClusterAPIControllers.TrustStatements...)
    57  	return policyDocument
    58  }
    59  
    60  func (t Template) controllersRolePolicy() []cfn_iam.Role_Policy {
    61  	policies := []cfn_iam.Role_Policy{}
    62  
    63  	if t.Spec.ClusterAPIControllers.ExtraStatements != nil {
    64  		policies = append(policies,
    65  			cfn_iam.Role_Policy{
    66  				PolicyName: t.Spec.StackName,
    67  				PolicyDocument: iamv1.PolicyDocument{
    68  					Statement: t.Spec.ClusterAPIControllers.ExtraStatements,
    69  					Version:   iamv1.CurrentVersion,
    70  				},
    71  			},
    72  		)
    73  	}
    74  	return policies
    75  }
    76  
    77  // ControllersPolicy will create a policy from a Template for AWS Controllers.
    78  func (t Template) ControllersPolicy() *iamv1.PolicyDocument {
    79  	statement := []iamv1.StatementEntry{
    80  		{
    81  			Effect:   iamv1.EffectAllow,
    82  			Resource: iamv1.Resources{iamv1.Any},
    83  			Action: iamv1.Actions{
    84  				"ec2:AllocateAddress",
    85  				"ec2:AssociateRouteTable",
    86  				"ec2:AttachInternetGateway",
    87  				"ec2:AuthorizeSecurityGroupIngress",
    88  				"ec2:CreateInternetGateway",
    89  				"ec2:CreateNatGateway",
    90  				"ec2:CreateRoute",
    91  				"ec2:CreateRouteTable",
    92  				"ec2:CreateSecurityGroup",
    93  				"ec2:CreateSubnet",
    94  				"ec2:CreateTags",
    95  				"ec2:CreateVpc",
    96  				"ec2:ModifyVpcAttribute",
    97  				"ec2:DeleteInternetGateway",
    98  				"ec2:DeleteNatGateway",
    99  				"ec2:DeleteRouteTable",
   100  				"ec2:ReplaceRoute",
   101  				"ec2:DeleteSecurityGroup",
   102  				"ec2:DeleteSubnet",
   103  				"ec2:DeleteTags",
   104  				"ec2:DeleteVpc",
   105  				"ec2:DescribeAccountAttributes",
   106  				"ec2:DescribeAddresses",
   107  				"ec2:DescribeAvailabilityZones",
   108  				"ec2:DescribeInstances",
   109  				"ec2:DescribeInternetGateways",
   110  				"ec2:DescribeImages",
   111  				"ec2:DescribeNatGateways",
   112  				"ec2:DescribeNetworkInterfaces",
   113  				"ec2:DescribeNetworkInterfaceAttribute",
   114  				"ec2:DescribeRouteTables",
   115  				"ec2:DescribeSecurityGroups",
   116  				"ec2:DescribeSubnets",
   117  				"ec2:DescribeVpcs",
   118  				"ec2:DescribeVpcAttribute",
   119  				"ec2:DescribeVolumes",
   120  				"ec2:DetachInternetGateway",
   121  				"ec2:DisassociateRouteTable",
   122  				"ec2:DisassociateAddress",
   123  				"ec2:ModifyInstanceAttribute",
   124  				"ec2:ModifyNetworkInterfaceAttribute",
   125  				"ec2:ModifySubnetAttribute",
   126  				"ec2:ReleaseAddress",
   127  				"ec2:RevokeSecurityGroupIngress",
   128  				"ec2:RunInstances",
   129  				"ec2:TerminateInstances",
   130  				"tag:GetResources",
   131  				"elasticloadbalancing:AddTags",
   132  				"elasticloadbalancing:CreateLoadBalancer",
   133  				"elasticloadbalancing:ConfigureHealthCheck",
   134  				"elasticloadbalancing:DeleteLoadBalancer",
   135  				"elasticloadbalancing:DeleteTargetGroup",
   136  				"elasticloadbalancing:DescribeLoadBalancers",
   137  				"elasticloadbalancing:DescribeLoadBalancerAttributes",
   138  				"elasticloadbalancing:ApplySecurityGroupsToLoadBalancer",
   139  				"elasticloadbalancing:DescribeTags",
   140  				"elasticloadbalancing:ModifyLoadBalancerAttributes",
   141  				"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
   142  				"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
   143  				"elasticloadbalancing:RemoveTags",
   144  				"autoscaling:DescribeAutoScalingGroups",
   145  				"autoscaling:DescribeInstanceRefreshes",
   146  				"ec2:CreateLaunchTemplate",
   147  				"ec2:CreateLaunchTemplateVersion",
   148  				"ec2:DescribeLaunchTemplates",
   149  				"ec2:DescribeLaunchTemplateVersions",
   150  				"ec2:DeleteLaunchTemplate",
   151  				"ec2:DeleteLaunchTemplateVersions",
   152  				"ec2:DescribeKeyPairs",
   153  			},
   154  		},
   155  		{
   156  			Effect: iamv1.EffectAllow,
   157  			Resource: iamv1.Resources{
   158  				"arn:*:autoscaling:*:*:autoScalingGroup:*:autoScalingGroupName/*",
   159  			},
   160  			Action: iamv1.Actions{
   161  				"autoscaling:CreateAutoScalingGroup",
   162  				"autoscaling:UpdateAutoScalingGroup",
   163  				"autoscaling:CreateOrUpdateTags",
   164  				"autoscaling:StartInstanceRefresh",
   165  				"autoscaling:DeleteAutoScalingGroup",
   166  				"autoscaling:DeleteTags",
   167  			},
   168  		},
   169  		{
   170  			Effect: iamv1.EffectAllow,
   171  			Resource: iamv1.Resources{
   172  				"arn:*:iam::*:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling",
   173  			},
   174  			Action: iamv1.Actions{
   175  				"iam:CreateServiceLinkedRole",
   176  			},
   177  			Condition: iamv1.Conditions{
   178  				iamv1.StringLike: map[string]string{"iam:AWSServiceName": "autoscaling.amazonaws.com"},
   179  			},
   180  		},
   181  		{
   182  			Effect: iamv1.EffectAllow,
   183  			Resource: iamv1.Resources{
   184  				"arn:*:iam::*:role/aws-service-role/elasticloadbalancing.amazonaws.com/AWSServiceRoleForElasticLoadBalancing",
   185  			},
   186  			Action: iamv1.Actions{
   187  				"iam:CreateServiceLinkedRole",
   188  			},
   189  			Condition: iamv1.Conditions{
   190  				iamv1.StringLike: map[string]string{"iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"},
   191  			},
   192  		},
   193  		{
   194  			Effect: iamv1.EffectAllow,
   195  			Action: iamv1.Actions{
   196  				"iam:CreateServiceLinkedRole",
   197  			},
   198  			Resource: iamv1.Resources{
   199  				"arn:*:iam::*:role/aws-service-role/spot.amazonaws.com/AWSServiceRoleForEC2Spot",
   200  			},
   201  			Condition: iamv1.Conditions{
   202  				iamv1.StringLike: map[string]string{"iam:AWSServiceName": "spot.amazonaws.com"},
   203  			},
   204  		},
   205  		{
   206  			Effect:   iamv1.EffectAllow,
   207  			Resource: t.allowedEC2InstanceProfiles(),
   208  			Action: iamv1.Actions{
   209  				"iam:PassRole",
   210  			},
   211  		},
   212  	}
   213  	for _, secureSecretBackend := range t.Spec.SecureSecretsBackends {
   214  		switch secureSecretBackend {
   215  		case infrav1.SecretBackendSecretsManager:
   216  			statement = append(statement, iamv1.StatementEntry{
   217  				Effect: iamv1.EffectAllow,
   218  				Resource: iamv1.Resources{
   219  					"arn:*:secretsmanager:*:*:secret:aws.cluster.x-k8s.io/*",
   220  				},
   221  				Action: iamv1.Actions{
   222  					"secretsmanager:CreateSecret",
   223  					"secretsmanager:DeleteSecret",
   224  					"secretsmanager:TagResource",
   225  				},
   226  			})
   227  		case infrav1.SecretBackendSSMParameterStore:
   228  			statement = append(statement, iamv1.StatementEntry{
   229  				Effect: iamv1.EffectAllow,
   230  				Resource: iamv1.Resources{
   231  					"arn:*:ssm:*:*:parameter/cluster.x-k8s.io/*",
   232  				},
   233  				Action: iamv1.Actions{
   234  					"ssm:PutParameter",
   235  					"ssm:DeleteParameter",
   236  					"ssm:AddTagsToResource",
   237  				},
   238  			})
   239  		}
   240  	}
   241  	if t.Spec.S3Buckets.Enable {
   242  		statement = append(statement, iamv1.StatementEntry{
   243  			Effect: iamv1.EffectAllow,
   244  			Resource: iamv1.Resources{
   245  				fmt.Sprintf("arn:*:s3:::%s*", t.Spec.S3Buckets.NamePrefix),
   246  			},
   247  			Action: iamv1.Actions{
   248  				"s3:CreateBucket",
   249  				"s3:DeleteBucket",
   250  				"s3:PutObject",
   251  				"s3:DeleteObject",
   252  				"s3:PutBucketPolicy",
   253  			},
   254  		})
   255  	}
   256  	if t.Spec.EventBridge.Enable {
   257  		statement = append(statement, iamv1.StatementEntry{
   258  			Effect:   iamv1.EffectAllow,
   259  			Resource: iamv1.Resources{iamv1.Any},
   260  			Action: iamv1.Actions{
   261  				"events:DeleteRule",
   262  				"events:DescribeRule",
   263  				"events:ListTargetsByRule",
   264  				"events:PutRule",
   265  				"events:PutTargets",
   266  				"events:RemoveTargets",
   267  				"sqs:CreateQueue",
   268  				"sqs:DeleteMessage",
   269  				"sqs:DeleteQueue",
   270  				"sqs:GetQueueAttributes",
   271  				"sqs:GetQueueUrl",
   272  				"sqs:ReceiveMessage",
   273  				"sqs:SetQueueAttributes",
   274  			},
   275  		})
   276  	}
   277  
   278  	return &iamv1.PolicyDocument{
   279  		Version:   iamv1.CurrentVersion,
   280  		Statement: statement,
   281  	}
   282  }
   283  
   284  // ControllersPolicyEKS creates a policy from a template for AWS Controllers.
   285  func (t Template) ControllersPolicyEKS() *iamv1.PolicyDocument {
   286  	statement := []iamv1.StatementEntry{}
   287  
   288  	allowedIAMActions := iamv1.Actions{
   289  		"iam:GetRole",
   290  		"iam:ListAttachedRolePolicies",
   291  	}
   292  	statement = append(statement, iamv1.StatementEntry{
   293  		Effect: iamv1.EffectAllow,
   294  		Resource: iamv1.Resources{
   295  			"arn:*:ssm:*:*:parameter/aws/service/eks/optimized-ami/*",
   296  		},
   297  		Action: iamv1.Actions{
   298  			"ssm:GetParameter",
   299  		},
   300  	})
   301  
   302  	statement = append(statement, iamv1.StatementEntry{
   303  		Effect: iamv1.EffectAllow,
   304  		Action: iamv1.Actions{
   305  			"iam:CreateServiceLinkedRole",
   306  		},
   307  		Resource: iamv1.Resources{
   308  			"arn:*:iam::*:role/aws-service-role/eks.amazonaws.com/AWSServiceRoleForAmazonEKS",
   309  		},
   310  		Condition: iamv1.Conditions{
   311  			iamv1.StringLike: map[string]string{"iam:AWSServiceName": "eks.amazonaws.com"},
   312  		},
   313  	})
   314  
   315  	statement = append(statement, iamv1.StatementEntry{
   316  		Effect: iamv1.EffectAllow,
   317  		Action: iamv1.Actions{
   318  			"iam:CreateServiceLinkedRole",
   319  		},
   320  		Resource: iamv1.Resources{
   321  			"arn:*:iam::*:role/aws-service-role/eks-nodegroup.amazonaws.com/AWSServiceRoleForAmazonEKSNodegroup",
   322  		},
   323  		Condition: iamv1.Conditions{
   324  			iamv1.StringLike: map[string]string{"iam:AWSServiceName": "eks-nodegroup.amazonaws.com"},
   325  		},
   326  	})
   327  
   328  	statement = append(statement, iamv1.StatementEntry{
   329  		Effect: iamv1.EffectAllow,
   330  		Action: iamv1.Actions{
   331  			"iam:CreateServiceLinkedRole",
   332  		},
   333  		Resource: iamv1.Resources{
   334  			"arn:aws:iam::*:role/aws-service-role/eks-fargate-pods.amazonaws.com/AWSServiceRoleForAmazonEKSForFargate",
   335  		},
   336  		Condition: iamv1.Conditions{
   337  			iamv1.StringLike: map[string]string{"iam:AWSServiceName": "eks-fargate.amazonaws.com"},
   338  		},
   339  	})
   340  
   341  	if t.Spec.EKS.AllowIAMRoleCreation {
   342  		allowedIAMActions = append(allowedIAMActions, iamv1.Actions{
   343  			"iam:DetachRolePolicy",
   344  			"iam:DeleteRole",
   345  			"iam:CreateRole",
   346  			"iam:TagRole",
   347  			"iam:AttachRolePolicy",
   348  		}...)
   349  
   350  		statement = append(statement, iamv1.StatementEntry{
   351  			Action: iamv1.Actions{
   352  				"iam:ListOpenIDConnectProviders",
   353  				"iam:CreateOpenIDConnectProvider",
   354  				"iam:AddClientIDToOpenIDConnectProvider",
   355  				"iam:UpdateOpenIDConnectProviderThumbprint",
   356  				"iam:DeleteOpenIDConnectProvider",
   357  			},
   358  			Resource: iamv1.Resources{
   359  				"*",
   360  			},
   361  			Effect: iamv1.EffectAllow,
   362  		})
   363  	}
   364  	statement = append(statement, []iamv1.StatementEntry{
   365  		{
   366  			Action: allowedIAMActions,
   367  			Resource: iamv1.Resources{
   368  				"arn:*:iam::*:role/*",
   369  			},
   370  			Effect: iamv1.EffectAllow,
   371  		}, {
   372  			Action: iamv1.Actions{
   373  				"iam:GetPolicy",
   374  			},
   375  			Resource: iamv1.Resources{
   376  				t.generateAWSManagedPolicyARN(eksClusterPolicyName),
   377  			},
   378  			Effect: iamv1.EffectAllow,
   379  		}, {
   380  			Action: iamv1.Actions{
   381  				"eks:DescribeCluster",
   382  				"eks:ListClusters",
   383  				"eks:CreateCluster",
   384  				"eks:TagResource",
   385  				"eks:UpdateClusterVersion",
   386  				"eks:DeleteCluster",
   387  				"eks:UpdateClusterConfig",
   388  				"eks:UntagResource",
   389  				"eks:UpdateNodegroupVersion",
   390  				"eks:DescribeNodegroup",
   391  				"eks:DeleteNodegroup",
   392  				"eks:UpdateNodegroupConfig",
   393  				"eks:CreateNodegroup",
   394  				"eks:AssociateEncryptionConfig",
   395  				"eks:ListIdentityProviderConfigs",
   396  				"eks:AssociateIdentityProviderConfig",
   397  				"eks:DescribeIdentityProviderConfig",
   398  				"eks:DisassociateIdentityProviderConfig",
   399  			},
   400  			Resource: iamv1.Resources{
   401  				"arn:*:eks:*:*:cluster/*",
   402  				"arn:*:eks:*:*:nodegroup/*/*/*",
   403  			},
   404  			Effect: iamv1.EffectAllow,
   405  		}, {
   406  			Action: iamv1.Actions{
   407  				"ec2:AssociateVpcCidrBlock",
   408  				"ec2:DisassociateVpcCidrBlock",
   409  				"eks:ListAddons",
   410  				"eks:CreateAddon",
   411  				"eks:DescribeAddonVersions",
   412  				"eks:DescribeAddon",
   413  				"eks:DeleteAddon",
   414  				"eks:UpdateAddon",
   415  				"eks:TagResource",
   416  				"eks:DescribeFargateProfile",
   417  				"eks:CreateFargateProfile",
   418  				"eks:DeleteFargateProfile",
   419  			},
   420  			Resource: iamv1.Resources{
   421  				"*",
   422  			},
   423  			Effect: iamv1.EffectAllow,
   424  		}, {
   425  			Action: iamv1.Actions{
   426  				"iam:PassRole",
   427  			},
   428  			Resource: iamv1.Resources{
   429  				"*",
   430  			},
   431  			Condition: iamv1.Conditions{
   432  				"StringEquals": map[string]string{
   433  					"iam:PassedToService": "eks.amazonaws.com",
   434  				},
   435  			},
   436  			Effect: iamv1.EffectAllow,
   437  		},
   438  		{
   439  			Action: iamv1.Actions{
   440  				"kms:CreateGrant",
   441  				"kms:DescribeKey",
   442  			},
   443  			Resource: iamv1.Resources{
   444  				"*",
   445  			},
   446  			Effect: iamv1.EffectAllow,
   447  			Condition: iamv1.Conditions{
   448  				"ForAnyValue:StringLike": map[string]string{
   449  					"kms:ResourceAliases": fmt.Sprintf("alias/%s", t.Spec.EKS.KMSAliasPrefix),
   450  				},
   451  			},
   452  		},
   453  	}...)
   454  
   455  	return &iamv1.PolicyDocument{
   456  		Version:   iamv1.CurrentVersion,
   457  		Statement: statement,
   458  	}
   459  }
   460  
   461  func (t Template) allowedEC2InstanceProfiles() iamv1.Resources {
   462  	if t.Spec.ClusterAPIControllers.AllowedEC2InstanceProfiles == nil {
   463  		t.Spec.ClusterAPIControllers.AllowedEC2InstanceProfiles = []string{
   464  			t.NewManagedName(iamv1.Any),
   465  		}
   466  	}
   467  	instanceProfiles := make(iamv1.Resources, len(t.Spec.ClusterAPIControllers.AllowedEC2InstanceProfiles))
   468  
   469  	for i, p := range t.Spec.ClusterAPIControllers.AllowedEC2InstanceProfiles {
   470  		instanceProfiles[i] = fmt.Sprintf("arn:*:iam::*:role/%s", p)
   471  	}
   472  
   473  	return instanceProfiles
   474  }