github.com/openshift/installer@v1.4.17/pkg/asset/installconfig/aws/permissions.go (about) 1 // Package aws collects AWS-specific configuration. 2 package aws 3 4 import ( 5 "errors" 6 "fmt" 7 8 "github.com/aws/aws-sdk-go/aws/session" 9 "github.com/sirupsen/logrus" 10 "k8s.io/apimachinery/pkg/util/sets" 11 12 ccaws "github.com/openshift/cloud-credential-operator/pkg/aws" 13 "github.com/openshift/installer/pkg/types" 14 "github.com/openshift/installer/pkg/types/aws" 15 ) 16 17 // PermissionGroup is the group of permissions needed by cluster creation, operation, or teardown. 18 type PermissionGroup string 19 20 const ( 21 // PermissionCreateBase is a base set of permissions required in all installs where the installer creates resources. 22 PermissionCreateBase PermissionGroup = "create-base" 23 24 // PermissionDeleteBase is a base set of permissions required in all installs where the installer deletes resources. 25 PermissionDeleteBase PermissionGroup = "delete-base" 26 27 // PermissionCreateNetworking is an additional set of permissions required when the installer creates networking resources. 28 PermissionCreateNetworking PermissionGroup = "create-networking" 29 30 // PermissionDeleteNetworking is a set of permissions required when the installer destroys networking resources. 31 PermissionDeleteNetworking PermissionGroup = "delete-networking" 32 33 // PermissionDeleteSharedNetworking is a set of permissions required when the installer destroys resources from a shared-network cluster. 34 PermissionDeleteSharedNetworking PermissionGroup = "delete-shared-networking" 35 36 // PermissionCreateInstanceRole is a set of permissions required when the installer creates instance roles. 37 PermissionCreateInstanceRole PermissionGroup = "create-instance-role" 38 39 // PermissionDeleteSharedInstanceRole is a set of permissions required when the installer destroys resources from a 40 // cluster with user-supplied IAM roles for instances. 41 PermissionDeleteSharedInstanceRole PermissionGroup = "delete-shared-instance-role" 42 43 // PermissionCreateInstanceProfile is a set of permission required when the installer creates instance profiles. 44 PermissionCreateInstanceProfile PermissionGroup = "create-instance-profile" 45 46 // PermissionDeleteSharedInstanceProfile is a set of permissions required when the installer destroys resources from 47 // a cluster with user-supplied IAM instance profiles for instances. 48 PermissionDeleteSharedInstanceProfile PermissionGroup = "delete-shared-instance-profile" 49 50 // PermissionCreateHostedZone is a set of permissions required when the installer creates a route53 hosted zone. 51 PermissionCreateHostedZone PermissionGroup = "create-hosted-zone" 52 53 // PermissionDeleteHostedZone is a set of permissions required when the installer destroys a route53 hosted zone. 54 PermissionDeleteHostedZone PermissionGroup = "delete-hosted-zone" 55 56 // PermissionKMSEncryptionKeys is an additional set of permissions required when the installer uses user provided kms encryption keys. 57 PermissionKMSEncryptionKeys PermissionGroup = "kms-encryption-keys" 58 59 // PermissionPublicIpv4Pool is an additional set of permissions required when the installer uses public IPv4 pools. 60 PermissionPublicIpv4Pool PermissionGroup = "public-ipv4-pool" 61 62 // PermissionDeleteIgnitionObjects is a permission set required when `preserveBootstrapIgnition` is not set. 63 PermissionDeleteIgnitionObjects PermissionGroup = "delete-ignition-objects" 64 ) 65 66 var permissions = map[PermissionGroup][]string{ 67 // Base set of permissions required for cluster creation 68 PermissionCreateBase: { 69 // EC2 related perms 70 "ec2:AuthorizeSecurityGroupEgress", 71 "ec2:AuthorizeSecurityGroupIngress", 72 "ec2:CopyImage", 73 "ec2:CreateNetworkInterface", 74 "ec2:AttachNetworkInterface", 75 "ec2:CreateSecurityGroup", 76 "ec2:CreateTags", 77 "ec2:CreateVolume", 78 "ec2:DeleteSecurityGroup", 79 "ec2:DeleteSnapshot", 80 "ec2:DeregisterImage", 81 "ec2:DescribeAccountAttributes", 82 "ec2:DescribeAddresses", 83 "ec2:DescribeAvailabilityZones", 84 "ec2:DescribeDhcpOptions", 85 "ec2:DescribeImages", 86 "ec2:DescribeInstanceAttribute", 87 "ec2:DescribeInstanceCreditSpecifications", 88 "ec2:DescribeInstances", 89 "ec2:DescribeInternetGateways", 90 "ec2:DescribeKeyPairs", 91 "ec2:DescribeNatGateways", 92 "ec2:DescribeNetworkAcls", 93 "ec2:DescribeNetworkInterfaces", 94 "ec2:DescribePrefixLists", 95 "ec2:DescribeRegions", 96 "ec2:DescribeRouteTables", 97 "ec2:DescribeSecurityGroups", 98 "ec2:DescribeSecurityGroupRules", 99 "ec2:DescribeSubnets", 100 "ec2:DescribeTags", 101 "ec2:DescribeVolumes", 102 "ec2:DescribeVpcAttribute", 103 "ec2:DescribeVpcClassicLink", 104 "ec2:DescribeVpcClassicLinkDnsSupport", 105 "ec2:DescribeVpcEndpoints", 106 "ec2:DescribeVpcs", 107 "ec2:GetEbsDefaultKmsKeyId", 108 "ec2:ModifyInstanceAttribute", 109 "ec2:ModifyNetworkInterfaceAttribute", 110 "ec2:RevokeSecurityGroupEgress", 111 "ec2:RevokeSecurityGroupIngress", 112 "ec2:RunInstances", 113 "ec2:TerminateInstances", 114 115 // ELB related perms 116 "elasticloadbalancing:AddTags", 117 "elasticloadbalancing:ApplySecurityGroupsToLoadBalancer", 118 "elasticloadbalancing:AttachLoadBalancerToSubnets", 119 "elasticloadbalancing:ConfigureHealthCheck", 120 "elasticloadbalancing:CreateListener", 121 "elasticloadbalancing:CreateLoadBalancer", 122 "elasticloadbalancing:CreateLoadBalancerListeners", 123 "elasticloadbalancing:CreateTargetGroup", 124 "elasticloadbalancing:DeleteLoadBalancer", 125 "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 126 "elasticloadbalancing:DeregisterTargets", 127 "elasticloadbalancing:DescribeInstanceHealth", 128 "elasticloadbalancing:DescribeListeners", 129 "elasticloadbalancing:DescribeLoadBalancerAttributes", 130 "elasticloadbalancing:DescribeLoadBalancers", 131 "elasticloadbalancing:DescribeTags", 132 "elasticloadbalancing:DescribeTargetGroupAttributes", 133 "elasticloadbalancing:DescribeTargetHealth", 134 "elasticloadbalancing:ModifyLoadBalancerAttributes", 135 "elasticloadbalancing:ModifyTargetGroup", 136 "elasticloadbalancing:ModifyTargetGroupAttributes", 137 "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 138 "elasticloadbalancing:RegisterTargets", 139 "elasticloadbalancing:SetLoadBalancerPoliciesOfListener", 140 "elasticloadbalancing:SetSecurityGroups", 141 142 // IAM related perms 143 "iam:GetInstanceProfile", 144 "iam:GetRole", 145 "iam:GetRolePolicy", 146 "iam:GetUser", 147 "iam:ListInstanceProfilesForRole", 148 "iam:ListRoles", 149 "iam:ListUsers", 150 "iam:PassRole", 151 "iam:SimulatePrincipalPolicy", 152 "iam:TagInstanceProfile", 153 "iam:TagRole", 154 155 // Route53 related perms 156 "route53:ChangeResourceRecordSets", 157 "route53:ChangeTagsForResource", 158 "route53:GetChange", 159 "route53:GetHostedZone", 160 "route53:ListHostedZones", 161 "route53:ListHostedZonesByName", 162 "route53:ListResourceRecordSets", 163 "route53:ListTagsForResource", 164 "route53:UpdateHostedZoneComment", 165 166 // S3 related perms 167 "s3:CreateBucket", 168 "s3:GetAccelerateConfiguration", 169 "s3:GetBucketAcl", 170 "s3:GetBucketCors", 171 "s3:GetBucketLocation", 172 "s3:GetBucketLogging", 173 "s3:GetBucketObjectLockConfiguration", 174 "s3:GetBucketPolicy", 175 "s3:GetBucketRequestPayment", 176 "s3:GetBucketTagging", 177 "s3:GetBucketVersioning", 178 "s3:GetBucketWebsite", 179 "s3:GetEncryptionConfiguration", 180 "s3:GetLifecycleConfiguration", 181 "s3:GetReplicationConfiguration", 182 "s3:ListBucket", 183 "s3:PutBucketAcl", 184 "s3:PutBucketPolicy", 185 "s3:PutBucketTagging", 186 "s3:PutEncryptionConfiguration", 187 188 // More S3 (would be nice to limit 'Resource' to just the bucket we actually interact with...) 189 "s3:GetObject", 190 "s3:GetObjectAcl", 191 "s3:GetObjectTagging", 192 "s3:GetObjectVersion", 193 "s3:PutObject", 194 "s3:PutObjectAcl", 195 "s3:PutObjectTagging", 196 }, 197 // Permissions required for deleting base cluster resources 198 PermissionDeleteBase: { 199 "autoscaling:DescribeAutoScalingGroups", 200 "ec2:DeleteNetworkInterface", 201 "ec2:DeletePlacementGroup", 202 "ec2:DeleteTags", 203 "ec2:DeleteVolume", 204 "elasticloadbalancing:DeleteTargetGroup", 205 "elasticloadbalancing:DescribeTargetGroups", 206 "iam:DeleteAccessKey", 207 "iam:DeleteUser", 208 "iam:ListAttachedRolePolicies", 209 "iam:ListInstanceProfiles", 210 "iam:ListRolePolicies", 211 "iam:ListUserPolicies", 212 "s3:DeleteBucket", 213 "s3:DeleteObject", 214 "s3:ListBucketVersions", 215 "tag:GetResources", 216 }, 217 // Permissions required for creating network resources 218 PermissionCreateNetworking: { 219 "ec2:AllocateAddress", 220 "ec2:AssociateDhcpOptions", 221 "ec2:AssociateAddress", 222 "ec2:AssociateRouteTable", 223 "ec2:AttachInternetGateway", 224 "ec2:CreateDhcpOptions", 225 "ec2:CreateInternetGateway", 226 "ec2:CreateNatGateway", 227 "ec2:CreateRoute", 228 "ec2:CreateRouteTable", 229 "ec2:CreateSubnet", 230 "ec2:CreateVpc", 231 "ec2:CreateVpcEndpoint", 232 "ec2:ModifySubnetAttribute", 233 "ec2:ModifyVpcAttribute", 234 }, 235 // Permissions required for deleting network resources 236 PermissionDeleteNetworking: { 237 "ec2:DeleteDhcpOptions", 238 "ec2:DeleteInternetGateway", 239 "ec2:DeleteNatGateway", 240 "ec2:DeleteRoute", 241 "ec2:DeleteRouteTable", 242 "ec2:DeleteSubnet", 243 "ec2:DeleteVpc", 244 "ec2:DeleteVpcEndpoints", 245 "ec2:DetachInternetGateway", 246 "ec2:DisassociateRouteTable", 247 "ec2:ReleaseAddress", 248 "ec2:ReplaceRouteTableAssociation", 249 }, 250 // Permissions required for deleting a cluster with shared network resources 251 PermissionDeleteSharedNetworking: { 252 "tag:UnTagResources", 253 }, 254 // Permissions required for creating an instance role 255 PermissionCreateInstanceRole: { 256 "iam:CreateRole", 257 "iam:DeleteRole", 258 "iam:DeleteRolePolicy", 259 "iam:PutRolePolicy", 260 }, 261 // Permissions required for deleting a cluster with shared instance roles 262 PermissionDeleteSharedInstanceRole: { 263 "iam:UntagRole", 264 }, 265 // Permissions required for creating an instance profile 266 PermissionCreateInstanceProfile: { 267 "iam:AddRoleToInstanceProfile", 268 "iam:CreateInstanceProfile", 269 "iam:DeleteInstanceProfile", 270 "iam:RemoveRoleFromInstanceProfile", 271 }, 272 // Permissions required for deleting a cluster with shared instance profiles 273 PermissionDeleteSharedInstanceProfile: { 274 "iam:UntagInstanceProfile", 275 }, 276 PermissionCreateHostedZone: { 277 "route53:CreateHostedZone", 278 }, 279 PermissionDeleteHostedZone: { 280 "route53:DeleteHostedZone", 281 }, 282 PermissionKMSEncryptionKeys: { 283 "kms:Decrypt", 284 "kms:Encrypt", 285 "kms:GenerateDataKey", 286 "kms:GenerateDataKeyWithoutPlainText", 287 "kms:DescribeKey", 288 "kms:RevokeGrant", 289 "kms:CreateGrant", 290 "kms:ListGrants", 291 }, 292 PermissionPublicIpv4Pool: { 293 // Needed to check the IP pools during install-config validation 294 "ec2:DescribePublicIpv4Pools", 295 // Needed by terraform because of bootstrap EIP created 296 "ec2:DisassociateAddress", 297 }, 298 PermissionDeleteIgnitionObjects: { 299 // Needed by terraform during the bootstrap destroy stage. 300 "s3:DeleteBucket", 301 // Needed by capa which always deletes the ignition objects once the VMs are up. 302 "s3:DeleteObject", 303 }, 304 } 305 306 // ValidateCreds will try to create an AWS session, and also verify that the current credentials 307 // are sufficient to perform an installation, and that they can be used for cluster runtime 308 // as either capable of creating new credentials for components that interact with the cloud or 309 // being able to be passed through as-is to the components that need cloud credentials 310 func ValidateCreds(ssn *session.Session, groups []PermissionGroup, region string) error { 311 requiredPermissions, err := PermissionsList(groups) 312 if err != nil { 313 return err 314 } 315 316 client := ccaws.NewClientFromSession(ssn) 317 318 sParams := &ccaws.SimulateParams{ 319 Region: region, 320 } 321 322 // Check whether we can do an installation 323 logger := logrus.StandardLogger() 324 canInstall, err := ccaws.CheckPermissionsAgainstActions(client, requiredPermissions, sParams, logger) 325 if err != nil { 326 return fmt.Errorf("checking install permissions: %w", err) 327 } 328 if !canInstall { 329 return errors.New("current credentials insufficient for performing cluster installation") 330 } 331 332 // Check whether we can mint new creds for cluster services needing to interact with the cloud 333 canMint, err := ccaws.CheckCloudCredCreation(client, logger) 334 if err != nil { 335 return fmt.Errorf("mint credentials check: %w", err) 336 } 337 if canMint { 338 return nil 339 } 340 341 // Check whether we can use the current credentials in passthrough mode to satisfy 342 // cluster services needing to interact with the cloud 343 canPassthrough, err := ccaws.CheckCloudCredPassthrough(client, sParams, logger) 344 if err != nil { 345 return fmt.Errorf("passthrough credentials check: %w", err) 346 } 347 if canPassthrough { 348 return nil 349 } 350 351 return errors.New("AWS credentials cannot be used to either create new creds or use as-is") 352 } 353 354 // RequiredPermissionGroups returns a set of required permissions for a given cluster configuration. 355 func RequiredPermissionGroups(ic *types.InstallConfig) []PermissionGroup { 356 permissionGroups := []PermissionGroup{PermissionCreateBase} 357 usingExistingVPC := len(ic.AWS.Subnets) != 0 358 usingExistingPrivateZone := len(ic.AWS.HostedZone) != 0 359 360 if !usingExistingVPC { 361 permissionGroups = append(permissionGroups, PermissionCreateNetworking) 362 } 363 364 if !usingExistingPrivateZone { 365 permissionGroups = append(permissionGroups, PermissionCreateHostedZone) 366 } 367 368 if includesKMSEncryptionKey(ic) { 369 logrus.Debugf("Adding %s to the group of permissions", PermissionKMSEncryptionKeys) 370 permissionGroups = append(permissionGroups, PermissionKMSEncryptionKeys) 371 } 372 373 // Add delete permissions for non-C2S installs. 374 if !aws.IsSecretRegion(ic.AWS.Region) { 375 permissionGroups = append(permissionGroups, PermissionDeleteBase) 376 if usingExistingVPC { 377 permissionGroups = append(permissionGroups, PermissionDeleteSharedNetworking) 378 } else { 379 permissionGroups = append(permissionGroups, PermissionDeleteNetworking) 380 } 381 if !usingExistingPrivateZone { 382 permissionGroups = append(permissionGroups, PermissionDeleteHostedZone) 383 } 384 } 385 386 if ic.AWS.PublicIpv4Pool != "" { 387 permissionGroups = append(permissionGroups, PermissionPublicIpv4Pool) 388 } 389 390 if !ic.AWS.BestEffortDeleteIgnition { 391 permissionGroups = append(permissionGroups, PermissionDeleteIgnitionObjects) 392 } 393 394 if includesCreateInstanceRole(ic) { 395 permissionGroups = append(permissionGroups, PermissionCreateInstanceRole) 396 } 397 398 if includesExistingInstanceRole(ic) { 399 permissionGroups = append(permissionGroups, PermissionDeleteSharedInstanceRole) 400 } 401 402 if includesExistingInstanceProfile(ic) { 403 permissionGroups = append(permissionGroups, PermissionDeleteSharedInstanceProfile) 404 } 405 406 if includesCreateInstanceProfile(ic) { 407 permissionGroups = append(permissionGroups, PermissionCreateInstanceProfile) 408 } 409 410 return permissionGroups 411 } 412 413 // PermissionsList compiles a list of permissions based on the permission groups provided. 414 func PermissionsList(required []PermissionGroup) ([]string, error) { 415 requiredPermissions := sets.New[string]() 416 for _, group := range required { 417 groupPerms, ok := permissions[group] 418 if !ok { 419 return nil, fmt.Errorf("unable to access permissions group %s", group) 420 } 421 requiredPermissions.Insert(groupPerms...) 422 } 423 424 return sets.List(requiredPermissions), nil 425 } 426 427 // includesExistingInstanceRole checks if at least one BYO instance role is included in the install-config. 428 func includesExistingInstanceRole(installConfig *types.InstallConfig) bool { 429 mpool := aws.MachinePool{} 430 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 431 432 if mp := installConfig.ControlPlane; mp != nil { 433 mpool.Set(mp.Platform.AWS) 434 } 435 436 for _, compute := range installConfig.Compute { 437 mpool.Set(compute.Platform.AWS) 438 } 439 440 return len(mpool.IAMRole) > 0 441 } 442 443 // includesCreateInstanceRole checks if at least one instance role will be created by the installer. 444 // Note: instance profiles have a role attached to them. 445 func includesCreateInstanceRole(installConfig *types.InstallConfig) bool { 446 { 447 mpool := aws.MachinePool{} 448 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 449 if mp := installConfig.ControlPlane; mp != nil { 450 mpool.Set(mp.Platform.AWS) 451 } 452 if len(mpool.IAMRole) == 0 && len(mpool.IAMProfile) == 0 { 453 return true 454 } 455 } 456 457 for _, compute := range installConfig.Compute { 458 mpool := aws.MachinePool{} 459 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 460 mpool.Set(compute.Platform.AWS) 461 if len(mpool.IAMRole) == 0 && len(mpool.IAMProfile) == 0 { 462 return true 463 } 464 } 465 466 if len(installConfig.Compute) > 0 { 467 return false 468 } 469 470 // If compute stanza is not defined, we know it'll inherit the value from DefaultMachinePlatform 471 mpool := aws.MachinePool{} 472 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 473 return len(mpool.IAMRole) == 0 && len(mpool.IAMProfile) == 0 474 } 475 476 // includesKMSEncryptionKey checks if any KMS encryption keys are included in the install-config. 477 func includesKMSEncryptionKey(installConfig *types.InstallConfig) bool { 478 mpool := aws.MachinePool{} 479 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 480 481 if mp := installConfig.ControlPlane; mp != nil { 482 mpool.Set(mp.Platform.AWS) 483 } 484 485 for _, compute := range installConfig.Compute { 486 mpool.Set(compute.Platform.AWS) 487 } 488 489 return len(mpool.KMSKeyARN) > 0 490 } 491 492 // includesExistingInstanceProfile checks if at least one BYO instance profile is included in the install-config. 493 func includesExistingInstanceProfile(installConfig *types.InstallConfig) bool { 494 mpool := aws.MachinePool{} 495 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 496 497 if mp := installConfig.ControlPlane; mp != nil { 498 mpool.Set(mp.Platform.AWS) 499 } 500 501 for _, compute := range installConfig.Compute { 502 mpool.Set(compute.Platform.AWS) 503 } 504 505 return len(mpool.IAMProfile) > 0 506 } 507 508 // includesCreateInstanceProfile checks if at least one instance profile will be created by the Installer. 509 func includesCreateInstanceProfile(installConfig *types.InstallConfig) bool { 510 { 511 mpool := aws.MachinePool{} 512 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 513 if mp := installConfig.ControlPlane; mp != nil { 514 mpool.Set(mp.Platform.AWS) 515 } 516 if len(mpool.IAMProfile) == 0 { 517 return true 518 } 519 } 520 521 for _, compute := range installConfig.Compute { 522 mpool := aws.MachinePool{} 523 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 524 mpool.Set(compute.Platform.AWS) 525 if len(mpool.IAMProfile) == 0 { 526 return true 527 } 528 } 529 530 if len(installConfig.Compute) > 0 { 531 return false 532 } 533 534 // If compute stanza is not defined, we know it'll inherit the value from DefaultMachinePlatform 535 mpool := aws.MachinePool{} 536 mpool.Set(installConfig.AWS.DefaultMachinePlatform) 537 return len(mpool.IAMProfile) == 0 538 }