sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/eks/roles.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 eks 18 19 import ( 20 "fmt" 21 22 "github.com/aws/aws-sdk-go/aws" 23 "github.com/aws/aws-sdk-go/aws/awserr" 24 "github.com/aws/aws-sdk-go/service/iam" 25 "github.com/pkg/errors" 26 27 ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1" 28 expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1" 29 eksiam "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/eks/iam" 30 "sigs.k8s.io/cluster-api-provider-aws/pkg/eks" 31 "sigs.k8s.io/cluster-api-provider-aws/pkg/record" 32 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 33 ) 34 35 const ( 36 maxIAMRoleNameLength = 64 37 ) 38 39 // NodegroupRolePolicies gives the policies required for a nodegroup role. 40 func NodegroupRolePolicies() []string { 41 return []string{ 42 "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", 43 "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", //TODO: Can remove when CAPA supports provisioning of OIDC web identity federation with service account token volume projection 44 "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", 45 } 46 } 47 48 // FargateRolePolicies gives the policies required for a fargate role. 49 func FargateRolePolicies() []string { 50 return []string{ 51 "arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy", 52 } 53 } 54 55 func (s *Service) reconcileControlPlaneIAMRole() error { 56 s.scope.V(2).Info("Reconciling EKS Control Plane IAM Role") 57 58 if s.scope.ControlPlane.Spec.RoleName == nil { 59 if !s.scope.EnableIAM() { 60 s.scope.Info("no eks control plane role specified, using default eks control plane role") 61 s.scope.ControlPlane.Spec.RoleName = &ekscontrolplanev1.DefaultEKSControlPlaneRole 62 } else { 63 s.scope.Info("no eks control plane role specified, using role based on cluster name") 64 s.scope.ControlPlane.Spec.RoleName = aws.String(fmt.Sprintf("%s-iam-service-role", s.scope.Name())) 65 } 66 } 67 s.scope.Info("using eks control plane role", "role-name", *s.scope.ControlPlane.Spec.RoleName) 68 69 role, err := s.GetIAMRole(*s.scope.ControlPlane.Spec.RoleName) 70 if err != nil { 71 if !isNotFound(err) { 72 return err 73 } 74 75 // If the disable IAM flag is used then the role must exist 76 if !s.scope.EnableIAM() { 77 return fmt.Errorf("getting role %s: %w", *s.scope.ControlPlane.Spec.RoleName, ErrClusterRoleNotFound) 78 } 79 80 role, err = s.CreateRole(*s.scope.ControlPlane.Spec.RoleName, s.scope.Name(), eksiam.ControlPlaneTrustRelationship(false), s.scope.AdditionalTags()) 81 if err != nil { 82 record.Warnf(s.scope.ControlPlane, "FailedIAMRoleCreation", "Failed to create control plane IAM role %q: %v", *s.scope.ControlPlane.Spec.RoleName, err) 83 84 return fmt.Errorf("creating role %s: %w", *s.scope.ControlPlane.Spec.RoleName, err) 85 } 86 record.Eventf(s.scope.ControlPlane, "SuccessfulIAMRoleCreation", "Created control plane IAM role %q", *s.scope.ControlPlane.Spec.RoleName) 87 } 88 89 if s.IsUnmanaged(role, s.scope.Name()) { 90 s.scope.V(2).Info("Skipping, EKS control plane role policy assignment as role is unamanged") 91 return nil 92 } 93 94 //TODO: check tags and trust relationship to see if they need updating 95 96 policies := []*string{ 97 aws.String("arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"), 98 } 99 if s.scope.ControlPlane.Spec.RoleAdditionalPolicies != nil { 100 if !s.scope.AllowAdditionalRoles() && len(*s.scope.ControlPlane.Spec.RoleAdditionalPolicies) > 0 { 101 return ErrCannotUseAdditionalRoles 102 } 103 104 for _, policy := range *s.scope.ControlPlane.Spec.RoleAdditionalPolicies { 105 additionalPolicy := policy 106 policies = append(policies, &additionalPolicy) 107 } 108 } 109 _, err = s.EnsurePoliciesAttached(role, policies) 110 if err != nil { 111 return errors.Wrapf(err, "error ensuring policies are attached: %v", policies) 112 } 113 114 return nil 115 } 116 117 func (s *Service) deleteControlPlaneIAMRole() error { 118 if s.scope.ControlPlane.Spec.RoleName == nil { 119 return nil 120 } 121 roleName := *s.scope.ControlPlane.Spec.RoleName 122 if !s.scope.EnableIAM() { 123 s.scope.V(2).Info("EKS IAM disabled, skipping deleting EKS Control Plane IAM Role") 124 return nil 125 } 126 127 s.scope.V(2).Info("Deleting EKS Control Plane IAM Role") 128 129 role, err := s.GetIAMRole(roleName) 130 if err != nil { 131 if isNotFound(err) { 132 s.V(2).Info("EKS Control Plane IAM Role already deleted") 133 return nil 134 } 135 136 return errors.Wrap(err, "getting eks control plane iam role") 137 } 138 139 if s.IsUnmanaged(role, s.scope.Name()) { 140 s.V(2).Info("Skipping, EKS control plane iam role deletion as role is unamanged") 141 return nil 142 } 143 144 err = s.DeleteRole(*s.scope.ControlPlane.Spec.RoleName) 145 if err != nil { 146 record.Eventf(s.scope.ControlPlane, "FailedIAMRoleDeletion", "Failed to delete control Plane IAM role %q: %v", *s.scope.ControlPlane.Spec.RoleName, err) 147 return err 148 } 149 150 record.Eventf(s.scope.ControlPlane, "SuccessfulIAMRoleDeletion", "Deleted Control Plane IAM role %q", *s.scope.ControlPlane.Spec.RoleName) 151 return nil 152 } 153 154 func (s *NodegroupService) reconcileNodegroupIAMRole() error { 155 s.scope.V(2).Info("Reconciling EKS Nodegroup IAM Role") 156 157 if s.scope.RoleName() == "" { 158 var roleName string 159 var err error 160 if !s.scope.EnableIAM() { 161 s.scope.Info("no EKS nodegroup role specified, using default EKS nodegroup role") 162 roleName = expinfrav1.DefaultEKSNodegroupRole 163 } else { 164 s.scope.Info("no EKS nodegroup role specified, using role based on nodegroup name") 165 roleName, err = eks.GenerateEKSName( 166 fmt.Sprintf("%s-%s", s.scope.KubernetesClusterName(), s.scope.NodegroupName()), 167 "-nodegroup-iam-service-role", 168 maxIAMRoleNameLength, 169 ) 170 if err != nil { 171 return errors.Wrap(err, "failed to generate IAM role name") 172 } 173 } 174 s.scope.ManagedMachinePool.Spec.RoleName = roleName 175 } 176 177 role, err := s.GetIAMRole(s.scope.RoleName()) 178 if err != nil { 179 if !isNotFound(err) { 180 return err 181 } 182 183 // If the disable IAM flag is used then the role must exist 184 if !s.scope.EnableIAM() { 185 return ErrNodegroupRoleNotFound 186 } 187 188 role, err = s.CreateRole(s.scope.ManagedMachinePool.Spec.RoleName, s.scope.ClusterName(), eksiam.NodegroupTrustRelationship(), s.scope.AdditionalTags()) 189 if err != nil { 190 record.Warnf(s.scope.ManagedMachinePool, "FailedIAMRoleCreation", "Failed to create nodegroup IAM role %q: %v", s.scope.RoleName(), err) 191 return err 192 } 193 record.Eventf(s.scope.ManagedMachinePool, "SuccessfulIAMRoleCreation", "Created nodegroup IAM role %q", s.scope.RoleName()) 194 } 195 196 if s.IsUnmanaged(role, s.scope.ClusterName()) { 197 s.scope.V(2).Info("Skipping, EKS nodegroup role policy assignment as role is unamanged") 198 return nil 199 } 200 201 _, err = s.EnsureTagsAndPolicy(role, s.scope.ClusterName(), eksiam.NodegroupTrustRelationship(), s.scope.AdditionalTags()) 202 if err != nil { 203 return errors.Wrapf(err, "error ensuring tags and policy document are set on node role") 204 } 205 206 policies := NodegroupRolePolicies() 207 if len(s.scope.ManagedMachinePool.Spec.RoleAdditionalPolicies) > 0 { 208 if !s.scope.AllowAdditionalRoles() { 209 return ErrCannotUseAdditionalRoles 210 } 211 212 policies = append(policies, s.scope.ManagedMachinePool.Spec.RoleAdditionalPolicies...) 213 } 214 215 _, err = s.EnsurePoliciesAttached(role, aws.StringSlice(policies)) 216 if err != nil { 217 return errors.Wrapf(err, "error ensuring policies are attached: %v", policies) 218 } 219 220 return nil 221 } 222 223 func (s *NodegroupService) deleteNodegroupIAMRole() (reterr error) { 224 if err := s.scope.IAMReadyFalse(clusterv1.DeletingReason, ""); err != nil { 225 return err 226 } 227 defer func() { 228 if reterr != nil { 229 record.Warnf( 230 s.scope.ManagedMachinePool, "FailedDeleteIAMNodegroupRole", "Failed to delete EKS nodegroup role %s: %v", s.scope.ManagedMachinePool.Spec.RoleName, reterr, 231 ) 232 if err := s.scope.IAMReadyFalse("DeletingFailed", reterr.Error()); err != nil { 233 reterr = err 234 } 235 } else if err := s.scope.IAMReadyFalse(clusterv1.DeletedReason, ""); err != nil { 236 reterr = err 237 } 238 }() 239 roleName := s.scope.RoleName() 240 if !s.scope.EnableIAM() { 241 s.scope.V(2).Info("EKS IAM disabled, skipping deleting EKS Nodegroup IAM Role") 242 return nil 243 } 244 245 s.scope.V(2).Info("Deleting EKS Nodegroup IAM Role") 246 247 role, err := s.GetIAMRole(roleName) 248 if err != nil { 249 if isNotFound(err) { 250 s.V(2).Info("EKS Nodegroup IAM Role already deleted") 251 return nil 252 } 253 254 return errors.Wrap(err, "getting EKS nodegroup iam role") 255 } 256 257 if s.IsUnmanaged(role, s.scope.ClusterName()) { 258 s.V(2).Info("Skipping, EKS Nodegroup iam role deletion as role is unamanged") 259 return nil 260 } 261 262 err = s.DeleteRole(s.scope.RoleName()) 263 if err != nil { 264 record.Eventf(s.scope.ManagedMachinePool, "FailedIAMRoleDeletion", "Failed to delete Nodegroup IAM role %q: %v", s.scope.ManagedMachinePool.Spec.RoleName, err) 265 return err 266 } 267 268 record.Eventf(s.scope.ManagedMachinePool, "SuccessfulIAMRoleDeletion", "Deleted Nodegroup IAM role %q", s.scope.ManagedMachinePool.Spec.RoleName) 269 return nil 270 } 271 272 func (s *FargateService) reconcileFargateIAMRole() (requeue bool, err error) { 273 s.scope.V(2).Info("Reconciling EKS Fargate IAM Role") 274 275 if s.scope.RoleName() == "" { 276 var roleName string 277 if !s.scope.EnableIAM() { 278 s.scope.Info("no EKS fargate role specified, using default EKS fargate role") 279 roleName = expinfrav1.DefaultEKSFargateRole 280 } else { 281 s.scope.Info("no EKS fargate role specified, using role based on fargate profile name") 282 roleName, err = eks.GenerateEKSName( 283 "fargate", 284 fmt.Sprintf("%s-%s", s.scope.KubernetesClusterName(), s.scope.FargateProfile.Spec.ProfileName), 285 maxIAMRoleNameLength, 286 ) 287 if err != nil { 288 return false, errors.Wrap(err, "couldn't generate IAM role name") 289 } 290 } 291 s.scope.FargateProfile.Spec.RoleName = roleName 292 return true, nil 293 } 294 295 var createdRole bool 296 297 role, err := s.GetIAMRole(s.scope.RoleName()) 298 if err != nil { 299 if !isNotFound(err) { 300 return false, err 301 } 302 303 // If the disable IAM flag is used then the role must exist 304 if !s.scope.EnableIAM() { 305 return false, ErrFargateRoleNotFound 306 } 307 308 createdRole = true 309 role, err = s.CreateRole(s.scope.RoleName(), s.scope.ClusterName(), eksiam.FargateTrustRelationship(), s.scope.AdditionalTags()) 310 if err != nil { 311 record.Warnf(s.scope.FargateProfile, "FailedIAMRoleCreation", "Failed to create fargate IAM role %q: %v", s.scope.RoleName(), err) 312 return false, errors.Wrap(err, "failed to create role") 313 } 314 record.Eventf(s.scope.FargateProfile, "SuccessfulIAMRoleCreation", "Created fargate IAM role %q", s.scope.RoleName()) 315 } 316 317 updatedRole, err := s.EnsureTagsAndPolicy(role, s.scope.ClusterName(), eksiam.FargateTrustRelationship(), s.scope.AdditionalTags()) 318 if err != nil { 319 return updatedRole, errors.Wrapf(err, "error ensuring tags and policy document are set on fargate role") 320 } 321 322 policies := FargateRolePolicies() 323 updatedPolicies, err := s.EnsurePoliciesAttached(role, aws.StringSlice(policies)) 324 if err != nil { 325 return updatedRole, errors.Wrapf(err, "error ensuring policies are attached: %v", policies) 326 } 327 328 return createdRole || updatedRole || updatedPolicies, nil 329 } 330 331 func (s *FargateService) deleteFargateIAMRole() (reterr error) { 332 if err := s.scope.IAMReadyFalse(clusterv1.DeletingReason, ""); err != nil { 333 return err 334 } 335 defer func() { 336 if reterr != nil { 337 record.Warnf( 338 s.scope.FargateProfile, "FailedIAMRoleDeletion", "Failed to delete EKS fargate role %s: %v", s.scope.FargateProfile.Spec.RoleName, reterr, 339 ) 340 if err := s.scope.IAMReadyFalse("DeletingFailed", reterr.Error()); err != nil { 341 reterr = err 342 } 343 } else if err := s.scope.IAMReadyFalse(clusterv1.DeletedReason, ""); err != nil { 344 reterr = err 345 } 346 }() 347 roleName := s.scope.RoleName() 348 if !s.scope.EnableIAM() { 349 s.scope.V(2).Info("EKS IAM disabled, skipping deleting EKS fargate IAM Role") 350 return nil 351 } 352 353 s.scope.V(2).Info("Deleting EKS fargate IAM Role") 354 355 _, err := s.GetIAMRole(roleName) 356 if err != nil { 357 if isNotFound(err) { 358 s.V(2).Info("EKS fargate IAM Role already deleted") 359 return nil 360 } 361 362 return errors.Wrap(err, "getting EKS fargate iam role") 363 } 364 365 err = s.DeleteRole(s.scope.RoleName()) 366 if err != nil { 367 record.Eventf(s.scope.FargateProfile, "FailedIAMRoleDeletion", "Failed to delete fargate IAM role %q: %v", s.scope.RoleName(), err) 368 return err 369 } 370 371 record.Eventf(s.scope.FargateProfile, "SuccessfulIAMRoleDeletion", "Deleted fargate IAM role %q", s.scope.RoleName()) 372 return nil 373 } 374 375 func isNotFound(err error) bool { 376 if aerr, ok := err.(awserr.Error); ok { 377 switch aerr.Code() { 378 case iam.ErrCodeNoSuchEntityException: 379 return true 380 default: 381 return false 382 } 383 } 384 385 return false 386 }