This post is part of an open-ended series I'm writing where I take a specific protocol, app, or whatever-I-feel-like and focus on five functional aspects of that thing in order to expose some of how that thing really works.

The topic in this post is the AWS Identity and Access Management (IAM) service. The IAM service holds a unique position within AWS: it doesn't get the attention that the machine learning or AI services get, and doesn't come to mind when buzzwords like "serverless" or "containers" are brought up, yet it's used by-or should be used by-every single AWS customer (and if you're not using it, you're not following best practice, tsk, tsk) so it's worthwhile to take the time to really get to know this service.

Let's begin!

1 - The root user supersedes IAM policies

The main reason I threw a bit of shade about following best practice and always using IAM has to do with the root user in an account. The root user is what's created when a new AWS account is opened. The username for the root user is always an email address and the root user is able to log into the AWS account as soon as the account is created. In other words, even before setting up accounts in IAM or setting up identity federation, the root user can log in. Put another way: it's the user that you log in as to bootstrap your entire AWS environment and start building things.

Another analogy would be that the AWS root user is akin to the root login on a Linux/BSD system or the Administrator account on a Windows system. On a freshly installed system, those accounts are the only ones that exist and so you log in with them in order to start setting things up, and hopefully, to then create less privileged users for day-to-day activities.

Much like the root account on a Linux/BSD system, the AWS root user has a very special property: it has implicit permission to every resource and every action associated with a resource within the account. It's all-powerful and has no limits. Further, limits cannot be imposed via IAM policy.

And therein lies the risk.

An all-powerful user very cleanly breaks the principle of least privilege. If an unauthorized party ever got access to that user, there's no limit to the impact they could have on that AWS account. Worse still:

  • An authorized individual using the root user for day-to-day activities has no guardrails to prevent destroying cloud resources due to a mis-click in the web console or a botched API call.
  • There's no audit trail that can tie back activities of the root user to an individual; so if an accident does happen, there's no audit trail to truly discover who exactly was logged in at the time.

For these reasons (and many more, I'm sure), best practice is to use the root user only long enough to create an administrative user within IAM, enable Multi-Factor Authentication (MFA) for the root user, and then lock away the MFA token and root password somewhere very secure and only use the root user in emergency situations.

2 - Testing IAM policies is done outside of the IAM console

AWS IAM policies are (typically) small JSON documents that define who can do what to which resources. For example, the identity-based policy below grants the user/group/role to which this policy is assigned full permissions to the Elastic Compute Cloud (EC2) service:

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

This is a very simple policy because it contains just a single Statement object and has used very broad wild cards for the Action and Resource parameters. A policy like this might be easily reasoned about just by looking at it, but in the case where the policy is more complex, having a way to test the policy to ensure it's granting and denying access as expected, is very, very helpful.

There is a way to test an IAM policy, but unexpectedly, it's not actually in the AWS IAM console. There's a separate tool called the IAM Policy Simulator which lets you test both policies that are live and attached to users/groups/roles already and new policies that you copy/paste into the tool for evaluation prior to attaching them to users/groups/roles.

Quoting from the IAM Policy Simulator docs:

"The simulator evaluates the policies that you choose and determines the effective permissions for each of the actions that you specify. The simulator uses the same policy evaluation engine that is used during real requests to AWS services.

A notable difference, however, is that the sim does not actually make API calls to the target services which allows you to safely test destructive API calls without worry.

3 - Policies Can Contain Granular Conditions

As sampled above, IAM identity-based policies declare who (the user/group/role that the policy is applied to) can do what Action to which Resource(s). These statements can be quite broad through the use of wild cards (as shown above) or very coarse by way of providing explicit values to each parameter.

That's good, but not good enough to properly express a well thought-out and highly descriptive access policy. For example, consider an access policy that includes language like this:

  • Calls to start/stop compute instances in the us-west-2 region must come from a trusted host in the 192.0.2.0/24 subnet.
  • Calls to terminate a database instance must come from a trusted host in the 192.0.2.0/24 subnet and be authenticated with Multi-Factor Authentication (MFA).

What we've looked at so far with IAM doesn't provide a way to specify a source IP address or type of authentication in the policy.

This is where IAM conditions come into play.

Conditions allow for the evaluation of keys that are included in the context of the API call that is being authorized. Keys in this sense carry information about the request or resources that the request references. There are a number of global keys that are present in every IAM authorization context and a set of service-specific keys which are only present in authorization contexts related to their respective service.

Below is an example that uses both global keys, such as aws:SourceIP, and service-specific keys, such as ec2:Regionto express a policy that matches the first bullet in the list above:

{
   "Version": "2012-10-17",
   "Statement": [
     {
       "Sid": "sid1",
       "Action": [
         "ec2:StartInstances",
         "ec2:StopInstances"
       ],
       "Effect": "Allow",
       "Resource": "*",
       "Condition": {
         "IpAddress": {
           "aws:SourceIp": "192.0.2.0/24"
         },
         "StringEquals": {
           "ec2:Region": "us-west-2"
         }
       }
     }
   ]
 }

There are important caveats to be aware of with some condition keys, aws:SourceIP being one of them. You'll want to read the documentation to ensure you're using them in the correct manner and not accidentally creating an IAM policy that won't work the way you intend.

Conditions can be used to create very intricate, very specific policies that allow the enforcement of rich access control beyond just "did the user authenticate with a valid username and password?"

4 - The Console's Search Box Makes Navigation Easy

You'd miss it if you weren't looking closely, but there is a small "Search IAM" box in the top-left corner of the AWS IAM console (at least, that's where the box is as of April 2019). The way the box blends into the page belies its power.

Beyond just searching for users, groups, and roles, the search box lets you search for access keys, documentation topics, and even tasks such as "create user". Even better, searching for a general word will return results across all of these categories. Here's an example of a search for the word blog in my account:

AWS IAM Console Search

The search results include a user (blogadmin), a role (S3AdminAccessBlogLogBuckets), and multiple tasks (eg, Add user blogadmin to groups). Each of the results is a hyperlink that will take you right to the page for that user/role/task/document.

The search box is a very speedy way to move around within IAM, especially if you have large numbers of users, groups, or roles. For more details on what type of searches you can do, see the AWS IAM documentation.

5 - IAM Groups Cannot be Principals

While AWS IAM does support the concept of groups, they are not true identities within IAM and cannot be used as the principal in an IAM policy. This can get a little confusing though so let me expand on what this means.

First, some more IAM theory. There are two types of policies in IAM: identity-based policies and resource-based policies.

The former type is attached to a principal (eg, a user, a compute instance, or a group) and specifies what permissions that principal has to resources that are listed in the policy. The policy written above which grants access to certain EC2 actions is an example of an identity-based policy. You can tell this because the policy contains a Resource key and does not contain a Principal key; the principal is identified by whatever identity the policy is attached to.

A resource-based policy is pretty much the opposite: it's attached to a resource (eg, an S3 bucket) and contains a Principal key. Example:

{
   "Version": "2012-10-17",
   "Statement": [
     {
       "Sid": "sid1",
       "Action": [
         "s3:PutObject",
         "s3:PutObjectAcl",
         "s3:PutObjectTagging"
       ],
       "Effect": "Allow",
       "Resource": "arn:aws:s3:::bloglogs",
       "Principal": {
         "AWS": [
           "blogadmin"
         ]
       }
     }
   ]
 }

Resource-based policies cannot specify a group in the list of principals but identity-based policies can be attached to groups. The solution here is to specify a role as the Principal in a resource-based policy and grant users membership to that role (or allow them to assume that role).

Conclusion

While AWS IAM may not be a commonly talked-about service, it may not be rife with trendy buzzwords, and it may not be the service that makes it into the latest cloud news, it is undoubtedly a service that every AWS customer uses when they provision a service (and when they access certain services). So understanding how best to leverage AWS IAM's features and functions will allow AWS customers to effectively control access to their cloud resources with a high degree of fidelity.

Thanks for reading. 😁

References


Disclaimer: The opinions and information expressed in this blog article are my own and not necessarily those of Amazon Web Services or Amazon, Inc.