Skip to content

Commit

Permalink
add credential validation
Browse files Browse the repository at this point in the history
do a pre-flight check of permissions using cloud-credentials-operator validation to do a check on the creds being used for installation

the initial list of permissions that gathers the AWS actions needed to perform an installation are taken verbatim from the IAM group permissions the hive team has been using to perform installation/uninstallation with (there absolutely could be some excess actions that used to be needed, but may no longer be needed)

note that the permissions checks are done with the assumption of IAM policies consisting of 'Resource: "*"'. so a list of ["ec2:CreateRoute", "ec2:CreateSubnet"] is evaluated as whether we can peform

`
{
    "Statement": [
        {
            "Action": [
                "ec2:CreateRoute",
                "ec2:CreateSubnet"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
`
  • Loading branch information
Joel Diaz committed Feb 14, 2019
1 parent c108005 commit 818b350
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 3 deletions.
222 changes: 220 additions & 2 deletions pkg/asset/installconfig/aws/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,224 @@
package aws

import (
_ "github.com/openshift/cloud-credential-operator/pkg/aws"
_ "github.com/openshift/cloud-credential-operator/pkg/controller/utils"
"github.com/aws/aws-sdk-go/aws/session"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"

ccaws "github.com/openshift/cloud-credential-operator/pkg/aws"
credvalidator "github.com/openshift/cloud-credential-operator/pkg/controller/utils"
)

var installPermissions = []string{
// EC2 related perms
"ec2:AllocateAddress",
"ec2:AssociateAddress",
"ec2:AssociateDhcpOptions",
"ec2:AssociateRouteTable",
"ec2:AttachInternetGateway",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateDhcpOptions",
"ec2:CreateInternetGateway",
"ec2:CreateNatGateway",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:CreateVpcEndpoint",
"ec2:CreateVolume",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeDhcpOptions",
"ec2:DescribeImages",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstanceCreditSpecifications",
"ec2:DescribeInstances",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkAcls",
"ec2:DescribePrefixLists",
"ec2:DescribeRegions",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcs",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVolumes",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:ModifyInstanceAttribute",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:RevokeSecurityGroupEgress",
"ec2:RunInstances",
"ec2:TerminateInstances",
"ec2:DeleteDhcpOptions",
"ec2:DeleteRoute",
"ec2:RevokeSecurityGroupIngress",
"ec2:DisassociateRouteTable",
"ec2:ReplaceRouteTableAssociation",
"ec2:DeleteRouteTable",
"ec2:DeleteSubnet",
"ec2:DescribeNetworkInterfaces",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:DeleteNatGateway",
"ec2:DeleteSecurityGroup",
"ec2:DetachInternetGateway",
"ec2:DeleteInternetGateway",
"ec2:ReleaseAddress",
"ec2:DeleteVpc",

// ELB related perms
"elasticloadbalancing:AddTags",
"elasticloadbalancing:ApplySecurityGroupsToLoadBalancer",
"elasticloadbalancing:AttachLoadBalancerToSubnets",
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateLoadBalancerListeners",
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:ConfigureHealthCheck",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:DescribeInstanceHealth",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeTags",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ModifyTargetGroup",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
"elasticloadbalancing:SetLoadBalancerPoliciesOfListener",

// IAM related perms
"iam:AddRoleToInstanceProfile",
"iam:CreateInstanceProfile",
"iam:CreateRole",
"iam:DeleteInstanceProfile",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:GetInstanceProfile",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:GetUser",
"iam:ListInstanceProfilesForRole",
"iam:ListRoles",
"iam:ListUsers",
"iam:PassRole",
"iam:PutRolePolicy",
"iam:RemoveRoleFromInstanceProfile",
"iam:SimulatePrincipalPolicy",
"iam:TagRole",

// Route53 related perms
"route53:ChangeResourceRecordSets",
"route53:ChangeTagsForResource",
"route53:GetChange",
"route53:GetHostedZone",
"route53:CreateHostedZone",
"route53:DeleteHostedZone",
"route53:ListHostedZones",
"route53:ListHostedZonesByName",
"route53:ListResourceRecordSets",
"route53:ListTagsForResource",
"route53:UpdateHostedZoneComment",

// S3 related perms
"s3:CreateBucket",
"s3:ListBucket",
"s3:GetBucketCors",
"s3:GetBucketWebsite",
"s3:GetBucketVersioning",
"s3:GetAccelerateConfiguration",
"s3:GetEncryptionConfiguration",
"s3:GetBucketRequestPayment",
"s3:GetBucketLogging",
"s3:GetLifecycleConfiguration",
"s3:GetBucketReplication",
"s3:GetReplicationConfiguration",
"s3:GetBucketLocation",
"s3:GetBucketTagging",
"s3:DeleteBucket",
"s3:PutBucketAcl",
"s3:PutBucketTagging",
"s3:PutEncryptionConfiguration",

// More S3 (would be nice to limit 'Resource' to just the bucket we actualy interact with...)
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectTagging",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectTagging",
"s3:GetObjectVersion",
"s3:DeleteObject",

// Uninstall-specific perms
"autoscaling:DescribeAutoScalingGroups",
"ec2:DeleteNetworkInterface",
"ec2:DeleteVolume",
"ec2:DeleteVpcEndpoints",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DeleteTargetGroup",
"iam:ListInstanceProfiles",
"iam:ListRolePolicies",
"iam:ListUserPolicies",
"tag:GetResources",
}

// ValidateCreds will try to create an AWS session, and also verify that the current credentials
// are sufficient to perform an installation, and that they can be used for cluster runtime
// as either capable of creating new credentials for components that interact with the cloud or
// being able to be passed through as-is to the components that need cloud credentials
func ValidateCreds(ssn *session.Session) error {
creds, err := ssn.Config.Credentials.Get()
if err != nil {
return errors.Wrap(err, "getting creds from session")
}

client, err := ccaws.NewClient([]byte(creds.AccessKeyID), []byte(creds.SecretAccessKey))
if err != nil {
return errors.Wrap(err, "initialize cloud-credentials client")
}

// Check whether we can do an installation
logger := logrus.New()
canInstall, err := credvalidator.CheckPermissionsAgainstActions(client, installPermissions, logger)
if err != nil {
return errors.Wrap(err, "checking install permissions")
}
if !canInstall {
return errors.New("current credentials insufficient for performing cluster installation")
}

// Check whether we can mint new creds for cluster services needing to interact with the cloud
canMint, err := credvalidator.CheckCloudCredCreation(client, logger)
if err != nil {
return errors.Wrap(err, "mint credentials check")
}

// Check whether we can use the current credentials in passthrough mode to satisfy
// cluster services needing to interact with the cloud
canPassthrough, err := credvalidator.CheckCloudCredPassthrough(client, logger)
if err != nil {
return errors.Wrap(err, "passthrough credentials check")
}

if !canMint && !canPassthrough {
return errors.New("AWS credentials cannot be used to either create new creds or use as-is")
}

return nil
}
10 changes: 9 additions & 1 deletion pkg/asset/installconfig/platformcredscheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/openshift/installer/pkg/types/libvirt"
"github.com/openshift/installer/pkg/types/none"
"github.com/openshift/installer/pkg/types/openstack"
"github.com/pkg/errors"
)

// PlatformCredsCheck is an asset that checks the platform credentials, asks for them or errors out if invalid
Expand All @@ -35,7 +36,14 @@ func (a *PlatformCredsCheck) Generate(dependencies asset.Parents) error {
platform := ic.Config.Platform.Name()
switch platform {
case aws.Name:
_, err = awsconfig.GetSession()
ssn, err := awsconfig.GetSession()
if err != nil {
return errors.Wrap(err, "creating AWS session")
}
err = awsconfig.ValidateCreds(ssn)
if err != nil {
return errors.Wrap(err, "validate AWS credentials")
}
case libvirt.Name:
case none.Name:
case openstack.Name:
Expand Down

0 comments on commit 818b350

Please sign in to comment.