One Cloud Please

Effective AWS IAM

18 August 2018

When creating a secure environment in AWS, IAM is a critical part of the security provided in the solution. It’s used for controlling how users compute resources and other services interact with each other, however it can also be the critical hole if you don’t properly secure your policies.

For users (and sometimes administrators), security is hard. It can slow you down with authentication and provide roadblocks when you are denied access to the resources you need. Many customer environments have experienced this and create dangerous holes for users. This can be an overzealous firewall rule, a shared credential or excessive privileges to perform the actions they need to do. It’s worth considering what the risks are when a business creates these shortcuts, as more and more companies are having big public security failures that crush reputations.

How IAM policies are evaluated

For developers that are new to working with IAM, the logic can be confusing at first, especially since the Amazon documentation is massive.

Here’s how I explain to others how IAM policy rules are applied:

  1. If a permission isn’t mentioned, it’s not given
  2. If a permission is given, that action is allowed UNLESS any other policy explicitly denies that permission.

With the way rules are evaluated, the order of how the rules are applied does not matter.

Open IAM Roles

Let’s look at an Amazon-provided role, ReadOnlyAccess.

This role gives the ability for a user to have read-only access to all resources within an AWS account, which is a privilege often given to users in order for them to view the resources within an AWS account. Though they have no access to perform any modifications directly, the scope of this role can unintentionally reveal information (unless explicitly denied elsewhere).

For example, this role grants permission to download all objects in every bucket in the account. Often, S3 objects may contain configuration information or even credentials that the developer may have thought secure. The role can also allow users to intercept SQS messages, get EC2 console output or get items from Kinesis or DynamoDB.

If you’re looking for a role which further restricts users’ access to the above resource, the ViewOnlyAccess role can alternatively be used, though you may find this to be too restrictive in some environments.

Conditional IAM Policies

One of the more powerful features of IAM policies is its ability to conditionally provide access to resources. This can help teams seperate themselves from other workloads or prevent unwanted actions. Here are some examples:

Tag-based Access

The below policy grants access to perform all actions, so long as it has the “Department” tag set to “Finance”. This is an easy way to segregate different parts of the business within the same account. Remember, not all services support tagging and account-wide limits still apply to everyone.

{
    "Version": "2012-10-17",
    "Statement": [ {
        "Effect": "Allow",
        "Action": [
            "*"
        ],
        "Resource": "*",
        "Condition": {
            "StringEquals": {
                "aws:RequestTag/Department": "Finance"
            }
        }
    } ]
}

Timeframe Restriction

The below policy grants access to perform all actions only within the timeframe shown. This is useful when users are only permitted to have access during certain periods, such as contractors.

{
    "Version": "2012-10-17",
    "Statement": [ {
        "Effect": "Allow",
        "Action": [
            "*"
        ],
        "Resource": "*",
        "Condition": {
            "DateGreaterThan": {"aws:CurrentTime": "2018-01-01T00:00:00Z"},
            "DateLessThan": {"aws:CurrentTime": "2018-02-31T23:59:59Z"}
        }
    } ]
}

IP-Based Restriction

The below policy grants access to perform all actions only when the request is made from the IP addresses specified. This can help restrict calls to only occur from within a corporate network, as an extra layer of security. Note that calls made by AWS services, such as CloudFormation when it creates resources, cannot be restricted in this way - however the call to create the stack could be.

{
    "Version": "2012-10-17",
    "Statement": [ {
        "Effect": "Allow",
        "Action": [
            "*"
        ],
        "Resource": "*",
        "Condition": {
            "IpAddress": {
                "aws:SourceIp": [
                    "1.2.3.4/24"
                ]
            }
        }
    } ]
}

Using permission boundaries

As of July 2018, IAM permission boundaries may be used to restrict the maximum permissions a user (or in some cases, a resource) can be assigned. If a permission boundary is set on an IAM user, the effective permissions that user has will always be the intersection of the permission boundary and their IAM policies. Here’s an example of how this works in practice:

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

The above policy is a simple example of what permissions a user might have. In this case, users can only perform S3 actions. Let’s say this policy was created with the name SimpleUserPolicy. Within this account, there is a person assigned to administer the creation of users. The policy assigned to their IAM user is as follows:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SimpleUserPermissions",
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": "*"
        },
        {
            "Sid": "CreateorChangeUser",
            "Effect": "Allow",
            "Action": [
                "iam:CreateUser",
                "iam:DeleteUserPolicy",
                "iam:AttachUserPolicy",
                "iam:DetachUserPolicy",
                "iam:PutUserPermissionsBoundary"
            ],
            "Resource": "*",
            "Condition": {"StringEquals": 
                {"iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/SimpleUserPolicy"}
            }
        },
        {
            "Sid": "IAMPermissions",
            "Effect": "Allow",
            "Action": [
                "iam:Get*",
                "iam:List*",
                "iam:Simulate*",
                "iam:DeleteUser",
                "iam:UpdateUser",
                "iam:CreateAccessKey",
                "iam:CreateLoginProfile"
            ],
            "Resource": "*"
        }
    ]
}

This policy grants the IAM user the same permissions as the other users (with the SimpleUserPermissions statement) as well as the ability to browse through and update user details with the IAMPermissions statement. Also granted, is the ability to create users or change their assigned policies with the CreateorChangeUser statement. Crucially, this statement has a condition that applies a permission boundary on the create/update process. The created user must be assigned the SimpleUserPolicy permission boundary or the create user call will fail.

With this, we can ensure that the created users permissions will never be escalated past the permission boundary set by the IAM user administrator.

Summary

IAM Roles and Policies are an important piece of every AWS environments security and when done correctly, can be a very powerful tool. However, these policies can very easily get out of control and can have unexpected consequences.