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  }