Identity and Access Management (IAM) - Practice Least Privilege


Chuck Ingram

IAM is the main source of access for your AWS account and ensuring that everything is secure is an important part of maintaining your environment.

Let’s say a new contractor is going to start working with your team and you’ve been tasked with creating a new user for them that allows them to perform specific EC2 and RDS actions within the four AWS accounts you use; Identity (where IAM users exist), Prod, Test, and Sandbox.

One way you might try to solve this would be to create a user for the Contractor in each account and assign the built-in AWS permissions for EC2 and RDS to the user. While this is a valid solution, it greatly increases the overhead associated with IAM administration.

Rather than having to manage individual IAM users, we’re going to use IAM Roles to provide temporary access to our AWS accounts.

Creating Cross Account Access IAM Roles

The first step is to create an IAM Role in our Production, Test, and Sandbox AWS accounts, which our contractor can assume to get temporary access to these accounts.

An IAM Role consists of several components. The first is the trust policy - this policy allows you to define who (or what) is able to assume the role. The second is the permissions policy - this policy defines what actions can be taken once the role has been assumed.

Trust Policy

In our particular scenario, we’d like to ensure that only the contractor is able to assume this role. Below is a code snippet of the IAM role defined via CloudFormation. The trust policy is defined in the AssumeRolePolicyDocument section.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
contractorRole:
    DependsOn:
      - contractorPolicy0
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
            - Effect: Allow
              Action:
                - sts:AssumeRole
              Principal:
                AWS:
                  - arn:aws:iam::123456789012:user/contractorUser
      Description: Allows CrossAccount access
      ManagedPolicyArns:
        - !Ref contractorPolicy0

Take specific note of the Principal section of the Role template. This Role is specifically designed to allow access only to specific users. The user ARNs in the Principal’s list allows you to limit who can assume this role. In our example, only our Contractor is able to use this role.

Permissions Policy

By knowing exactly what services our contractor willaccess we can tailor our permissions policy to grant just enough access in each AWS account for their job role.

In our example, we’ll need to provide specific actions for EC2 and read-only access to RDS. Below is a snippet of the IAM permissions policy we created.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
contractorPolicy0:
    Type: AWS::IAM::ManagedPolicy
Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - ec2:RunInstances
              - ec2:RebootInstances
              - ec2:StartInstances
              - ec2:StopInstances
           - ec2:Describe*
           - ec2:Get*
           - ec2:List*
            Resource: "*"
            Condition:
              BoolIfExists:
                aws:MultiFactorAuthPresent: 'true'
          - Effect: Allow
            Action:
           - rds:Describe*
           - rds:Get*
           - rds:List*
            Resource: "*"
            Condition:
              BoolIfExists:
                aws:MultiFactorAuthPresent: 'true'

To keep the administration overhead of IAM as low as possible, we’ll assign permissions via IAM Groups, rather than directly to IAM users.

Here’s a code snippet that creates the IAM Group and associates the IAM Policy.

1
2
3
4
5
6
7
contractorGroup0:
    Type: AWS::IAM::Group
    DependsOn:
      - contractorPolicy0
    Properties:
      ManagedPolicyArns:
        - !Ref contractorPolicy0

Let’s walk through what’s happening in the above code snippets.

First, we create an IAM Managed Policy named contractorPolicy0. Unlike an in-line policy, a Managed Policy can be attached to multiple IAM resources like Users and Roles.

In our policy, we’ve created multiple statements. I recommend grouping actions for the same service together.

The first statement contains the EC2 actions our contractor is able to perform, including starting, stopping, and restarting instances. The other actions, Describe, Get, and List, are all common Read Only actions. These also have trailing wildcard suffixes that allow all actions with that prefix to be executed.

You’ll also notice an optional property “Condition”. Conditions allow you to add an additional layer of security in your IAM permissions. Essentially, IAM evaluates conditions as boolean statements and they must evaluate to true in order for the policy to be applied.

We’ve also created an IAM Group defined in ‘contractorGroup0’. To reduce the amount of administration in IAM, we assigned policies via groups rather than at the user level. In the CloudFormation code, we associate the policy statement we defined earlier with the ‘ManagedPolicyArns’ attribute.

Allowing Cross Account Access

Once both Roles have been created a final Policy and Group combination needs to be created in the Prod account, or whichever account your Users are populated in.

Let’s take a look at the CloudFormation template:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
contractorCrossPolicy0:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Resource:
              - arn:aws:iam::432187652109:role/test-role
           - arn:aws:iam::111122223333:role/sandbox-role
            Condition:
              Bool:
                aws:MultiFactorAuthPresent: true

  contractorCrossGroup0:
    Type: AWS::IAM::Group
    DependsOn:
      - contractorCrossPolicy0
    Properties:
      ManagedPolicyArns:
        - !Ref contractorCrossPolicy0

This template creates a Policy and Group that grants users access to the Roles that have been created in the other accounts. Users must then be added to the contractorCrossGroup that is created before the two Role URLs that were collected earlier will work.

Scenario Wrap Up

Now in our scenario we have created a Policy and Group in Prod that allows specific AWS Resource access. Additional access was created in Test and Sandbox using the same Policy permissions access, and with Roles created that allows our User access to those accounts. Lastly, an additional Policy and Group were created in Prod that allows users listed in the Cross Account Access Group access to the Test and Sandbox accounts. The four CloudFormation stacks deployed now cover all the access requirements of our scenario.


Chuck Ingram


Orbit

Like what you read? Why not subscribe to the weekly Orbit newsletter and get content before everyone else?