AWS IAM in a layman's terms (3) -- IAM policy setup to give development team maximal velocity and autonomy

We alluded in one of our previous post that the development team will own a lot of responsibility defining application related resource access control, simply because the dev team owns the infrastructure as code (IaC) responsibility themselves.

No matter how well security savvy and security-educated a development team is, the central security team still needs some control, some kind of “trust but verify”. In this post, we briefly talk about leveraging the right type of AWS IAM policy mechanisms to build the responsibility separation between the “central” team and the individual “development” team. Here central and individual are relative: it can be the official central official IT security team vs. individual product line; and it can be the official DevSecOp platform team vs. individual two-pizza team.

IAM policy types categorized in 1000 feet

First of all there are several types of IAM policies and it’s a bit intimidating to the beginners to figure out how to use them. When an engineer started using AWS, it is even hard to understand why AWS provides so many different types of IAM policy mechanism at the first place. There are a lot of articles on the Internet talking about how to evaluate policy and how to write policies to protect resource — that’s not the focus of this post.

In this post, we wanted to give a 1000 feet high view of these types of policies, and using an example to explain why the exist at the first place.

On the left side of the diagram, a central team who may not necessarily know every details of the application’s needs can define their access control policies; and on the right side of the diagram, individual application owners can define the policies related their apps.

An example: combining boundary and identity based policy

Let’s use an example to illustrate how permission boundary policy and identity-based policy working together.

Imagine the security team knows for sure (and therefore define it through policy) that a user or a group of users that they can only operate on the following resources:

  1. Cloudwatch;

  2. S3

  3. EC2;

Then they can define the permission boundary accordingly:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:*",
                "cloudwatch:*",
                "ec2:*"
            ],
            "Resource": "*"
        }
    ]
}

When the application team accidentally allows the user or the group of users to have the privilege of “creating an IAM user” below.

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "iam:CreateUser",
    "Resource": "*"
  }
}

Then the permission leakage won’t happen, which is reflected in the following diagram: the privilege must be within the intersect of (security team’s) permission boundary and (an accidentally defined) identity-base policy.

Another example: combining Organization SCP and identity-base policy

When the org grows bigger and we may define organizations and give dedicated AWS account to a specific team. And we can assign Organizations service control policy (SCP) to define the maximum permissions that the members of a specific account could possibly have.

In the following Organization SCP policy, we make sure users can’t launch EC2 instances that are more expensive than medium size (there is no need for a developer to launch a 2xlarge instance to see if his code works).

  {
            "Sid": "limitedSize",
            "Effect": "Deny",
            "Action": "ec2:RunInstances",
            "Resource": "arn:aws:ec2:*:*:instance/*",
            "Condition": {
                "ForAnyValue:StringNotLike": {
                    "ec2:InstanceType": [
                        "*.nano",
                        "*.small",
                        "*.micro",
                        "*.medium"
                    ]
                }
            }
        }

But within the dev account, our dev account admin defined a group of people to have certain privilege (identity-based policy) — see policy attached to Group 1 in the dev account below:

{
   "Version": "2012-10-17",
   "Statement": [{
      "Effect": "Allow",
      "Action": [
         "ec2:*", 
      ],
      "Resource": "*"
   }
   ]
}

The identity-based policy allow pretty much every action toward EC2 here. But since our organization SCP deny’s launching EC2 instance more expensive than medium size, an user in dev account can’t launch an EC2 machine of xlarge, which is an obvious waste of money unnecessarily 😊.

Summary:

With different types of policy, we can give team a lot of autonomy and velocity yet we can still maintain control. And we provided two simple examples to illustrate the use cases above.

Hopefully we give a plain English explanation why permission boundary and organization SCP policy exist. If you have any question and experience to share about how a small team can setup some security boundary yet give the individual feature team maximal velocity, please leave your comments.