@@ -7,8 +7,11 @@ import (
77
88 "github.com/aws/aws-sdk-go/aws/session"
99 "github.com/sirupsen/logrus"
10+ "k8s.io/apimachinery/pkg/util/sets"
1011
1112 ccaws "github.com/openshift/cloud-credential-operator/pkg/aws"
13+ "github.com/openshift/installer/pkg/types"
14+ "github.com/openshift/installer/pkg/types/aws"
1215)
1316
1417// PermissionGroup is the group of permissions needed by cluster creation, operation, or teardown.
@@ -30,6 +33,9 @@ const (
3033 // PermissionDeleteSharedNetworking is a set of permissions required when the installer destroys resources from a shared-network cluster.
3134 PermissionDeleteSharedNetworking PermissionGroup = "delete-shared-networking"
3235
36+ // PermissionCreateInstanceRole is a set of permissions required when the installer creates instance roles.
37+ PermissionCreateInstanceRole PermissionGroup = "create-instance-role"
38+
3339 // PermissionDeleteSharedInstanceRole is a set of permissions required when the installer destroys resources from a
3440 // cluster with user-supplied IAM roles for instances.
3541 PermissionDeleteSharedInstanceRole PermissionGroup = "delete-shared-instance-role"
@@ -129,10 +135,7 @@ var permissions = map[PermissionGroup][]string{
129135 // IAM related perms
130136 "iam:AddRoleToInstanceProfile" ,
131137 "iam:CreateInstanceProfile" ,
132- "iam:CreateRole" ,
133138 "iam:DeleteInstanceProfile" ,
134- "iam:DeleteRole" ,
135- "iam:DeleteRolePolicy" ,
136139 "iam:GetInstanceProfile" ,
137140 "iam:GetRole" ,
138141 "iam:GetRolePolicy" ,
@@ -141,7 +144,6 @@ var permissions = map[PermissionGroup][]string{
141144 "iam:ListRoles" ,
142145 "iam:ListUsers" ,
143146 "iam:PassRole" ,
144- "iam:PutRolePolicy" ,
145147 "iam:RemoveRoleFromInstanceProfile" ,
146148 "iam:SimulatePrincipalPolicy" ,
147149 "iam:TagInstanceProfile" ,
@@ -246,6 +248,13 @@ var permissions = map[PermissionGroup][]string{
246248 PermissionDeleteSharedNetworking : {
247249 "tag:UnTagResources" ,
248250 },
251+ // Permissions required for creating an instance role
252+ PermissionCreateInstanceRole : {
253+ "iam:CreateRole" ,
254+ "iam:DeleteRole" ,
255+ "iam:DeleteRolePolicy" ,
256+ "iam:PutRolePolicy" ,
257+ },
249258 // Permissions required for deleting a cluster with shared instance roles
250259 PermissionDeleteSharedInstanceRole : {
251260 "iam:UntagRole" ,
@@ -285,14 +294,9 @@ var permissions = map[PermissionGroup][]string{
285294// as either capable of creating new credentials for components that interact with the cloud or
286295// being able to be passed through as-is to the components that need cloud credentials
287296func ValidateCreds (ssn * session.Session , groups []PermissionGroup , region string ) error {
288- // Compile a list of permissions based on the permission groups provided
289- requiredPermissions := []string {}
290- for _ , group := range groups {
291- groupPerms , ok := permissions [group ]
292- if ! ok {
293- return fmt .Errorf ("unable to access permissions group %s" , group )
294- }
295- requiredPermissions = append (requiredPermissions , groupPerms ... )
297+ requiredPermissions , err := PermissionsList (groups )
298+ if err != nil {
299+ return err
296300 }
297301
298302 client := ccaws .NewClientFromSession (ssn )
@@ -332,3 +336,132 @@ func ValidateCreds(ssn *session.Session, groups []PermissionGroup, region string
332336
333337 return errors .New ("AWS credentials cannot be used to either create new creds or use as-is" )
334338}
339+
340+ // RequiredPermissionGroups returns a set of required permissions for a given cluster configuration.
341+ func RequiredPermissionGroups (ic * types.InstallConfig ) []PermissionGroup {
342+ permissionGroups := []PermissionGroup {PermissionCreateBase }
343+ usingExistingVPC := len (ic .AWS .Subnets ) != 0
344+ usingExistingPrivateZone := len (ic .AWS .HostedZone ) != 0
345+
346+ if ! usingExistingVPC {
347+ permissionGroups = append (permissionGroups , PermissionCreateNetworking )
348+ }
349+
350+ if ! usingExistingPrivateZone {
351+ permissionGroups = append (permissionGroups , PermissionCreateHostedZone )
352+ }
353+
354+ if includesKMSEncryptionKey (ic ) {
355+ logrus .Debugf ("Adding %s to the group of permissions" , PermissionKMSEncryptionKeys )
356+ permissionGroups = append (permissionGroups , PermissionKMSEncryptionKeys )
357+ }
358+
359+ // Add delete permissions for non-C2S installs.
360+ if ! aws .IsSecretRegion (ic .AWS .Region ) {
361+ permissionGroups = append (permissionGroups , PermissionDeleteBase )
362+ if usingExistingVPC {
363+ permissionGroups = append (permissionGroups , PermissionDeleteSharedNetworking )
364+ } else {
365+ permissionGroups = append (permissionGroups , PermissionDeleteNetworking )
366+ }
367+ if ! usingExistingPrivateZone {
368+ permissionGroups = append (permissionGroups , PermissionDeleteHostedZone )
369+ }
370+ }
371+
372+ if ic .AWS .PublicIpv4Pool != "" {
373+ permissionGroups = append (permissionGroups , PermissionPublicIpv4Pool )
374+ }
375+
376+ if ! ic .AWS .BestEffortDeleteIgnition {
377+ permissionGroups = append (permissionGroups , PermissionDeleteIgnitionObjects )
378+ }
379+
380+ if includesCreateInstanceRole (ic ) {
381+ permissionGroups = append (permissionGroups , PermissionCreateInstanceRole )
382+ }
383+
384+ if includesExistingInstanceRole (ic ) {
385+ permissionGroups = append (permissionGroups , PermissionDeleteSharedInstanceRole )
386+ }
387+
388+ return permissionGroups
389+ }
390+
391+ // PermissionsList compiles a list of permissions based on the permission groups provided.
392+ func PermissionsList (required []PermissionGroup ) ([]string , error ) {
393+ requiredPermissions := sets .New [string ]()
394+ for _ , group := range required {
395+ groupPerms , ok := permissions [group ]
396+ if ! ok {
397+ return nil , fmt .Errorf ("unable to access permissions group %s" , group )
398+ }
399+ requiredPermissions .Insert (groupPerms ... )
400+ }
401+
402+ return sets .List (requiredPermissions ), nil
403+ }
404+
405+ // includesExistingInstanceRole checks if at least one BYO instance role is included in the install-config.
406+ func includesExistingInstanceRole (installConfig * types.InstallConfig ) bool {
407+ mpool := aws.MachinePool {}
408+ mpool .Set (installConfig .AWS .DefaultMachinePlatform )
409+
410+ if mp := installConfig .ControlPlane ; mp != nil {
411+ mpool .Set (mp .Platform .AWS )
412+ }
413+
414+ for _ , compute := range installConfig .Compute {
415+ mpool .Set (compute .Platform .AWS )
416+ }
417+
418+ return len (mpool .IAMRole ) > 0
419+ }
420+
421+ // includesCreateInstanceRole checks if at least one instance role will be created by the installer.
422+ func includesCreateInstanceRole (installConfig * types.InstallConfig ) bool {
423+ {
424+ mpool := aws.MachinePool {}
425+ mpool .Set (installConfig .AWS .DefaultMachinePlatform )
426+ if mp := installConfig .ControlPlane ; mp != nil {
427+ mpool .Set (mp .Platform .AWS )
428+ }
429+ if len (mpool .IAMRole ) == 0 {
430+ return true
431+ }
432+ }
433+
434+ for _ , compute := range installConfig .Compute {
435+ mpool := aws.MachinePool {}
436+ mpool .Set (installConfig .AWS .DefaultMachinePlatform )
437+ mpool .Set (compute .Platform .AWS )
438+ if len (mpool .IAMRole ) == 0 {
439+ return true
440+ }
441+ }
442+
443+ if len (installConfig .Compute ) > 0 {
444+ return false
445+ }
446+
447+ // If compute stanza is not defined, we know it'll inherit the value from DefaultMachinePlatform
448+ mpool := aws.MachinePool {}
449+ mpool .Set (installConfig .AWS .DefaultMachinePlatform )
450+ return len (mpool .IAMRole ) == 0
451+ }
452+
453+ // includesKMSEncryptionKey checks if any KMS encryption keys are included in the install-config.
454+ func includesKMSEncryptionKey (installConfig * types.InstallConfig ) bool {
455+ mpool := aws.MachinePool {}
456+ mpool .Set (installConfig .AWS .DefaultMachinePlatform )
457+
458+ if mp := installConfig .ControlPlane ; mp != nil {
459+ mpool .Set (mp .Platform .AWS )
460+ }
461+
462+ for _ , compute := range installConfig .Compute {
463+ mpool .Set (compute .Platform .AWS )
464+ }
465+
466+ return len (mpool .KMSKeyARN ) > 0
467+ }
0 commit comments