sigs.k8s.io/cluster-api-provider-aws@v1.5.5/cmd/clusterawsadm/cloudformation/bootstrap/template.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  	bootstrapv1 "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/api/bootstrap/v1beta1"
    26  	"sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/converters"
    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  	iamv1 "sigs.k8s.io/cluster-api-provider-aws/iam/api/v1beta1"
    30  	eksiam "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/services/eks/iam"
    31  )
    32  
    33  // Constants that define resources for a Template.
    34  const (
    35  	AWSIAMGroupBootstrapper                      = "AWSIAMGroupBootstrapper"
    36  	AWSIAMInstanceProfileControllers             = "AWSIAMInstanceProfileControllers"
    37  	AWSIAMInstanceProfileControlPlane            = "AWSIAMInstanceProfileControlPlane"
    38  	AWSIAMInstanceProfileNodes                   = "AWSIAMInstanceProfileNodes"
    39  	AWSIAMRoleControllers                        = "AWSIAMRoleControllers"
    40  	AWSIAMRoleControlPlane                       = "AWSIAMRoleControlPlane"
    41  	AWSIAMRoleNodes                              = "AWSIAMRoleNodes"
    42  	AWSIAMRoleEKSControlPlane                    = "AWSIAMRoleEKSControlPlane"
    43  	AWSIAMRoleEKSNodegroup                       = "AWSIAMRoleEKSNodegroup"
    44  	AWSIAMRoleEKSFargate                         = "AWSIAMRoleEKSFargate"
    45  	AWSIAMUserBootstrapper                       = "AWSIAMUserBootstrapper"
    46  	ControllersPolicy                 PolicyName = "AWSIAMManagedPolicyControllers"
    47  	ControllersPolicyEKS              PolicyName = "AWSIAMManagedPolicyControllersEKS"
    48  	ControlPlanePolicy                PolicyName = "AWSIAMManagedPolicyCloudProviderControlPlane"
    49  	NodePolicy                        PolicyName = "AWSIAMManagedPolicyCloudProviderNodes"
    50  	CSIPolicy                         PolicyName = "AWSEBSCSIPolicyController"
    51  	EKSConsolePolicy                  PolicyName = "AWSIAMManagedPolicyEKSConsole"
    52  )
    53  
    54  // Template is an AWS CloudFormation template to bootstrap
    55  // IAM policies, users and roles for use by Cluster API Provider AWS.
    56  type Template struct {
    57  	Spec *bootstrapv1.AWSIAMConfigurationSpec
    58  }
    59  
    60  // NewTemplate will generate a new Template.
    61  func NewTemplate() Template {
    62  	conf := bootstrapv1.NewAWSIAMConfiguration()
    63  	return Template{
    64  		Spec: &conf.Spec,
    65  	}
    66  }
    67  
    68  // NewManagedName creates an IAM acceptable name prefixed with this Cluster API
    69  // implementation's prefix.
    70  func (t Template) NewManagedName(name string) string {
    71  	return fmt.Sprintf("%s%s%s", t.Spec.NamePrefix, name, *t.Spec.NameSuffix)
    72  }
    73  
    74  // RenderCloudFormation will render and return a cloudformation Template.
    75  func (t Template) RenderCloudFormation() *cloudformation.Template {
    76  	template := cloudformation.NewTemplate()
    77  
    78  	if t.Spec.BootstrapUser.Enable {
    79  		template.Resources[AWSIAMUserBootstrapper] = &cfn_iam.User{
    80  			UserName:          t.Spec.BootstrapUser.UserName,
    81  			Groups:            t.bootstrapUserGroups(),
    82  			ManagedPolicyArns: t.Spec.ControlPlane.ExtraPolicyAttachments,
    83  			Policies:          t.bootstrapUserPolicy(),
    84  			Tags:              converters.MapToCloudFormationTags(t.Spec.BootstrapUser.Tags),
    85  		}
    86  
    87  		template.Resources[AWSIAMGroupBootstrapper] = &cfn_iam.Group{
    88  			GroupName: t.Spec.BootstrapUser.GroupName,
    89  		}
    90  	}
    91  
    92  	template.Resources[string(ControllersPolicy)] = &cfn_iam.ManagedPolicy{
    93  		ManagedPolicyName: t.NewManagedName("controllers"),
    94  		Description:       `For the Kubernetes Cluster API Provider AWS Controllers`,
    95  		PolicyDocument:    t.ControllersPolicy(),
    96  		Groups:            t.controllersPolicyGroups(),
    97  		Roles:             t.controllersPolicyRoleAttachments(),
    98  	}
    99  
   100  	if !t.Spec.EKS.Disable {
   101  		template.Resources[string(ControllersPolicyEKS)] = &cfn_iam.ManagedPolicy{
   102  			ManagedPolicyName: t.NewManagedName("controllers-eks"),
   103  			Description:       `For the Kubernetes Cluster API Provider AWS Controllers`,
   104  			PolicyDocument:    t.ControllersPolicyEKS(),
   105  			Groups:            t.controllersPolicyGroups(),
   106  			Roles:             t.controllersPolicyRoleAttachments(),
   107  		}
   108  	}
   109  
   110  	if !t.Spec.ControlPlane.DisableCloudProviderPolicy {
   111  		template.Resources[string(ControlPlanePolicy)] = &cfn_iam.ManagedPolicy{
   112  			ManagedPolicyName: t.NewManagedName("control-plane"),
   113  			Description:       `For the Kubernetes Cloud Provider AWS Control Plane`,
   114  			PolicyDocument:    t.cloudProviderControlPlaneAwsPolicy(),
   115  			Roles:             t.cloudProviderControlPlaneAwsRoles(),
   116  		}
   117  	}
   118  
   119  	if !t.Spec.Nodes.DisableCloudProviderPolicy {
   120  		template.Resources[string(NodePolicy)] = &cfn_iam.ManagedPolicy{
   121  			ManagedPolicyName: t.NewManagedName("nodes"),
   122  			Description:       `For the Kubernetes Cloud Provider AWS nodes`,
   123  			PolicyDocument:    t.nodePolicy(),
   124  			Roles:             t.cloudProviderNodeAwsRoles(),
   125  		}
   126  	}
   127  
   128  	if t.Spec.ControlPlane.EnableCSIPolicy {
   129  		template.Resources[string(CSIPolicy)] = &cfn_iam.ManagedPolicy{
   130  			ManagedPolicyName: t.NewManagedName("csi"),
   131  			Description:       `For the AWS EBS CSI Driver for Kubernetes`,
   132  			PolicyDocument:    t.csiControllerPolicy(),
   133  			Roles:             t.csiControlPlaneAwsRoles(),
   134  		}
   135  	}
   136  
   137  	template.Resources[AWSIAMRoleControlPlane] = &cfn_iam.Role{
   138  		RoleName:                 t.NewManagedName("control-plane"),
   139  		AssumeRolePolicyDocument: t.controlPlaneTrustPolicy(),
   140  		ManagedPolicyArns:        t.Spec.ControlPlane.ExtraPolicyAttachments,
   141  		Policies:                 t.controlPlanePolicies(),
   142  		Tags:                     converters.MapToCloudFormationTags(t.Spec.ControlPlane.Tags),
   143  	}
   144  
   145  	template.Resources[AWSIAMRoleControllers] = &cfn_iam.Role{
   146  		RoleName:                 t.NewManagedName("controllers"),
   147  		AssumeRolePolicyDocument: t.controllersTrustPolicy(),
   148  		Policies:                 t.controllersRolePolicy(),
   149  		Tags:                     converters.MapToCloudFormationTags(t.Spec.ClusterAPIControllers.Tags),
   150  	}
   151  
   152  	template.Resources[AWSIAMRoleNodes] = &cfn_iam.Role{
   153  		RoleName:                 t.NewManagedName("nodes"),
   154  		AssumeRolePolicyDocument: t.nodeTrustPolicy(),
   155  		ManagedPolicyArns:        t.nodeManagedPolicies(),
   156  		Policies:                 t.nodePolicies(),
   157  		Tags:                     converters.MapToCloudFormationTags(t.Spec.Nodes.Tags),
   158  	}
   159  
   160  	template.Resources[AWSIAMInstanceProfileControlPlane] = &cfn_iam.InstanceProfile{
   161  		InstanceProfileName: t.NewManagedName("control-plane"),
   162  		Roles: []string{
   163  			cloudformation.Ref(AWSIAMRoleControlPlane),
   164  		},
   165  	}
   166  
   167  	template.Resources[AWSIAMInstanceProfileControllers] = &cfn_iam.InstanceProfile{
   168  		InstanceProfileName: t.NewManagedName("controllers"),
   169  		Roles: []string{
   170  			cloudformation.Ref(AWSIAMRoleControllers),
   171  		},
   172  	}
   173  
   174  	template.Resources[AWSIAMInstanceProfileNodes] = &cfn_iam.InstanceProfile{
   175  		InstanceProfileName: t.NewManagedName("nodes"),
   176  		Roles: []string{
   177  			cloudformation.Ref(AWSIAMRoleNodes),
   178  		},
   179  	}
   180  
   181  	if !t.Spec.EKS.DefaultControlPlaneRole.Disable && !t.Spec.EKS.Disable {
   182  		template.Resources[AWSIAMRoleEKSControlPlane] = &cfn_iam.Role{
   183  			RoleName:                 ekscontrolplanev1.DefaultEKSControlPlaneRole,
   184  			AssumeRolePolicyDocument: AssumeRolePolicy(iamv1.PrincipalService, []string{"eks.amazonaws.com"}),
   185  			ManagedPolicyArns:        t.eksControlPlanePolicies(),
   186  			Tags:                     converters.MapToCloudFormationTags(t.Spec.EKS.DefaultControlPlaneRole.Tags),
   187  		}
   188  	}
   189  
   190  	if !t.Spec.EKS.ManagedMachinePool.Disable && !t.Spec.EKS.Disable {
   191  		template.Resources[AWSIAMRoleEKSNodegroup] = &cfn_iam.Role{
   192  			RoleName:                 expinfrav1.DefaultEKSNodegroupRole,
   193  			AssumeRolePolicyDocument: AssumeRolePolicy(iamv1.PrincipalService, []string{"ec2.amazonaws.com", "eks.amazonaws.com"}),
   194  			ManagedPolicyArns:        t.eksMachinePoolPolicies(),
   195  			Tags:                     converters.MapToCloudFormationTags(t.Spec.EKS.ManagedMachinePool.Tags),
   196  		}
   197  	}
   198  
   199  	if !t.Spec.EKS.Fargate.Disable && !t.Spec.EKS.Disable {
   200  		template.Resources[AWSIAMRoleEKSFargate] = &cfn_iam.Role{
   201  			RoleName:                 expinfrav1.DefaultEKSFargateRole,
   202  			AssumeRolePolicyDocument: AssumeRolePolicy(iamv1.PrincipalService, []string{eksiam.EKSFargateService}),
   203  			ManagedPolicyArns:        fargateProfilePolicies(t.Spec.EKS.Fargate),
   204  			Tags:                     converters.MapToCloudFormationTags(t.Spec.EKS.Fargate.Tags),
   205  		}
   206  	}
   207  
   208  	if t.Spec.EKS.EnableUserEKSConsolePolicy && !t.Spec.EKS.Disable {
   209  		template.Resources[string(EKSConsolePolicy)] = &cfn_iam.ManagedPolicy{
   210  			ManagedPolicyName: t.NewManagedName("eks-console"),
   211  			Description:       `For users/groups to view EKS nodes and workloads`,
   212  			PolicyDocument:    t.eksConsolePolicies(),
   213  		}
   214  	}
   215  
   216  	return template
   217  }
   218  
   219  func ec2AssumeRolePolicy() *iamv1.PolicyDocument {
   220  	return AssumeRolePolicy(iamv1.PrincipalService, []string{"ec2.amazonaws.com"})
   221  }
   222  
   223  // AWSArnAssumeRolePolicy will assume Policies using PolicyArns.
   224  func AWSArnAssumeRolePolicy(identityID string) *iamv1.PolicyDocument {
   225  	return AssumeRolePolicy(iamv1.PrincipalAWS, []string{identityID})
   226  }
   227  
   228  // AWSServiceAssumeRolePolicy will assume an AWS Service policy.
   229  func AWSServiceAssumeRolePolicy(identityID string) *iamv1.PolicyDocument {
   230  	return AssumeRolePolicy(iamv1.PrincipalService, []string{identityID})
   231  }
   232  
   233  // AssumeRolePolicy will create a role session and pass session policies programmatically.
   234  func AssumeRolePolicy(identityType iamv1.PrincipalType, principalIDs []string) *iamv1.PolicyDocument {
   235  	return &iamv1.PolicyDocument{
   236  		Version: iamv1.CurrentVersion,
   237  		Statement: []iamv1.StatementEntry{
   238  			{
   239  				Effect:    iamv1.EffectAllow,
   240  				Principal: iamv1.Principals{identityType: principalIDs},
   241  				Action:    iamv1.Actions{"sts:AssumeRole"},
   242  			},
   243  		},
   244  	}
   245  }