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 }