Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Tighter IAM policies #612

Closed

Conversation

randomvariable
Copy link
Member

@randomvariable randomvariable commented Feb 26, 2019

What this PR does / why we need it:

EC2 and ELB instances are tagged upon creation. Therefore, ensuring that only EC2/ELB instances are deleted when they match the Cluster API Provider AWS tag minimises privileges.

This approach cannot be used for other resources as they are tagged after creation - backing out of a failed tagging may prevent deletion, and controller can tag a resource appropriately to delete it.

Also additionally constrain which tags are allowed to be used by the controller.

Which issue(s) this PR fixes (optional, in fixes #<issue number>(, fixes #<issue_number>, ...) format, will close the issue(s) when PR gets merged):
Fixes #608

Special notes for your reviewer:

In order to restrict privilege escalation via instance profile, all resources are now put under a sigs.k8s.io/cluster-api-provider-aws path so this can be referenced in a policy condition. Unfortunately, this breaks CloudFormation being able to do an in-place update. There doesn't seem to be a way around this, so have also taken the opportunity to rename two incorrectly named resources. These are in the release notes below.

Recommend this is not merged prior to e2e tests making use of CloudFormation with a delete process.

Current example policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                }
            },
            "Action": [
                "ec2:AllocateAddress",
                "ec2:AssociateRouteTable",
                "ec2:AttachInternetGateway",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateInternetGateway",
                "ec2:CreateNatGateway",
                "ec2:CreateRoute",
                "ec2:CreateRouteTable",
                "ec2:CreateSecurityGroup",
                "ec2:CreateSubnet",
                "ec2:CreateVpc",
                "ec2:DeleteInternetGateway",
                "ec2:DeleteNatGateway",
                "ec2:DeleteRouteTable",
                "ec2:DeleteSecurityGroup",
                "ec2:DeleteSubnet",
                "ec2:DeleteVpc",
                "ec2:DescribeAddresses",
                "ec2:DescribeAvailabilityZones",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeImages",
                "ec2:DescribeNatGateways",
                "ec2:DescribeRouteTables",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcs",
                "ec2:DetachInternetGateway",
                "ec2:DisassociateRouteTable",
                "ec2:ModifySubnetAttribute",
                "ec2:ReleaseAddress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"
                },
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                }
            },
            "Action": [
                "iam:CreateServiceLinkedRole"
            ],
            "Resource": [
                "arn:aws:iam::xxxxxxxx:role/aws-service-role/elasticloadbalancing.amazonaws.com/AWSServiceRoleForElasticLoadBalancing"
            ],
            "Effect": "Allow"
        },
        {
            "Condition": {
                "StringEquals": {
                    "aws:RequestTag/sigs.k8s.io/cluster-api-provider-aws/managed": [
                        "true"
                    ],
                    "aws:RequestTag/sigs.k8s.io/cluster-api-provider-aws/role": [
                        "apiserver",
                        "bastion",
                        "common",
                        "private",
                        "public"
                    ]
                },
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                },
                "ForAnyValue:StringLike": {
                    "aws:TagKeys": [
                        "kubernetes.io/cluster/*",
                        "sigs.k8s.io/cluster-api-provider-aws/managed",
                        "sigs.k8s.io/cluster-api-provider-aws/role",
                        "Name"
                    ]
                }
            },
            "Action": [
                "ec2:CreateTags",
                "ec2:RunInstances",
                "elasticloadbalancing:CreateLoadBalancer"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Condition": {
                "StringEquals": {
                    "aws:RequestTag/sigs.k8s.io/cluster-api-provider-aws/managed": [
                        "true"
                    ],
                    "aws:RequestTag/sigs.k8s.io/cluster-api-provider-aws/role": [
                        "apiserver",
                        "bastion",
                        "common",
                        "private",
                        "public"
                    ]
                },
                "StringLike": {
                    "ec2:InstanceProfile": "arn:aws:iam::xxxxxxxx:instance-profile/sigs.k8s.io/cluster-api-provider-aws/*"
                },
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                },
                "ForAnyValue:StringLike": {
                    "aws:TagKeys": [
                        "kubernetes.io/cluster/*",
                        "sigs.k8s.io/cluster-api-provider-aws/managed",
                        "sigs.k8s.io/cluster-api-provider-aws/role",
                        "Name"
                    ]
                }
            },
            "Action": [
                "ec2:RunInstances"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Condition": {
                "StringEquals": {
                    "aws:RequestTag/sigs.k8s.io/cluster-api-provider-aws/managed": [
                        "true"
                    ],
                    "aws:RequestTag/sigs.k8s.io/cluster-api-provider-aws/role": [
                        "apiserver",
                        "bastion",
                        "common",
                        "private",
                        "public"
                    ],
                    "ec2:ResourceTag/sigs.k8s.io/cluster-api-provider-aws/managed": [
                        "true"
                    ]
                },
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                },
                "ForAnyValue:StringLike": {
                    "aws:TagKeys": [
                        "kubernetes.io/cluster/*",
                        "sigs.k8s.io/cluster-api-provider-aws/managed",
                        "sigs.k8s.io/cluster-api-provider-aws/role",
                        "Name"
                    ]
                }
            },
            "Action": [
                "ec2:CreateTags"
            ],
            "Resource": [
                "arn:aws:ec2:::instance/*"
            ],
            "Effect": "Allow"
        },
        {
            "Condition": {
                "StringEquals": {
                    "ec2:ResourceTag/sigs.k8s.io/cluster-api-provider-aws/managed": [
                        "true"
                    ]
                },
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                }
            },
            "Action": [
                "ec2:TerminateInstances",
                "ec2:DescribeInstances"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Condition": {
                "StringEquals": {
                    "elasticloadbalancing:ResourceTag/sigs.k8s.io/cluster-api-provider-aws/managed": [
                        "true"
                    ]
                },
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                }
            },
            "Action": [
                "elasticloadbalancing:DeleteLoadBalancer",
                "elasticloadbalancing:ConfigureHealthCheck",
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:DescribeLoadBalancerAttributes",
                "elasticloadbalancing:ModifyLoadBalancerAttributes",
                "elasticloadbalancing:RegisterInstancesWithLoadBalancer"
            ],
            "Resource": [
                "*"
            ],
            "Effect": "Allow"
        },
        {
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": [
                        "true"
                    ]
                }
            },
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                 "arn:aws:iam::xxxxxxxx:role/sigs.k8s.io/cluster-api-provider-aws/*"
            ],
            "Effect": "Allow"
        }
    ]
}

/hold

Please confirm that if this PR changes any image versions, then that's the sole change this PR makes.

Release note:

* Reduced privileges required by the controllers to delete EC2 and ELB instances.
* The `nodes.cluster-api-provider-aws.sigs.k8s.io` policy has been renamed `nodes.cloud-provider-aws.k8s.io` to better represent its purpose.
* The `control-plane.cluster-api-provider-aws.sigs.k8s.io` policy has been renamed `control-plane.cloud-provider-aws.k8s.io` to better represent its purpose.

Breaking changes:
 * clusterawsadm now places all IAM resources under a `/sigs.k8s.io/cluster-api-provider-aws` path, which AWS CloudFormation cannot do an in-place update for. Please delete the `cluster-api-provider-aws-sigs-k8s-io`  stack and replace it.

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Feb 26, 2019
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Feb 26, 2019
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: randomvariable

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Feb 26, 2019
EC2 and ELB instances are tagged upon creation. Therefore,
ensuring that only EC2/ELB instances are deleted when they match
the Cluster API Provider AWS tag minimises privileges.

This approach cannot be used for other resources as they are tagged
after creation.

Signed-off-by: Naadir Jeewa <[email protected]>
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Feb 26, 2019
@k8s-ci-robot k8s-ci-robot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Feb 26, 2019
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Feb 26, 2019
@randomvariable randomvariable changed the title Tighter IAM policies [WIP] Tighter IAM policies Feb 26, 2019
@k8s-ci-robot k8s-ci-robot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 26, 2019
@randomvariable
Copy link
Member Author

randomvariable commented Feb 26, 2019

Believe this preserves the intent of the current controllers now.

The main advantage with this PR is that EC2 & ELB instance modification and deletions of things which do not belong to Cluster API should be blocked.

Until AWS supports tagging on creation for the other objects, ability to limit the scope of actions to only cluster API managed objects will be limited.

Now need to add e2e to this.

@randomvariable
Copy link
Member Author

TODO: Restrict ec2:InstanceProfile on ec2:RunInstance to prevent privilege escalation.

@detiber
Copy link
Member

detiber commented Feb 26, 2019

TODO: Restrict ec2:InstanceProfile on ec2:RunInstance to prevent privilege escalation.

I'm actually wondering if it would make sense to leverage InstanceProfiles when a MachineClass is in use?

@randomvariable
Copy link
Member Author

I'm actually wondering if it would make sense to leverage InstanceProfiles when a MachineClass is in use?

Just want to check. InstanceProfile is what EC2 uses to reference IAM roles for machines, I would have thought it's Launch Templates which is the analog to MachineClasses. Maybe worth using the latter with MachineClasses then. No strong opinion.

@detiber
Copy link
Member

detiber commented Feb 26, 2019

I'm actually wondering if it would make sense to leverage InstanceProfiles when a MachineClass is in use?

Just want to check. InstanceProfile is what EC2 uses to reference IAM roles for machines, I would have thought it's Launch Templates which is the analog to MachineClasses. Maybe worth using the latter with MachineClasses then. No strong opinion.

Hah, that's what I get for commenting during the first half of the day. Launch Templates was indeed what I was thinking of.

@randomvariable
Copy link
Member Author

ec2:InstanceProfile restrictions added, but requires breaking changes to CloudFormation (in release notes).

In order to restrict privilege escalation via instance profile, all resources
are now put under a `sigs.k8s.io/cluster-api-provider-aws` path so this can be
referenced in a policy condition. Unfortunately, this breaks CloudFormation
being able to do an in-place update. There doesn't seem to be a way around this,
so have also taken the opportunity to rename two incorrectly named resources.

Signed-off-by: Naadir Jeewa <[email protected]>
@vincepri
Copy link
Member

vincepri commented Mar 7, 2019

/milestone v1alpha1

@k8s-ci-robot k8s-ci-robot added this to the v1alpha1 milestone Mar 7, 2019
@randomvariable
Copy link
Member Author

@vincepri this is actually
/milestone next

@k8s-ci-robot
Copy link
Contributor

@randomvariable: You must be a member of the kubernetes-sigs/cluster-api-provider-aws-maintainers GitHub team to set the milestone. If you believe you should be able to issue the /milestone command, please contact your Cluster API Provider AWS Maintainers and have them propose you as an additional delegate for this responsibility.

In response to this:

@vincepri this is actually
/milestone next

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@detiber
Copy link
Member

detiber commented Mar 7, 2019

/milestone Next

@randomvariable
Copy link
Member Author

Closing this for now, as can't actively work on it at the moment.

@randomvariable randomvariable deleted the tighter-iam branch May 26, 2020 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Restrict IAM policies further to only allow deletion (and possibly update) of resources
4 participants