Are you getting the most out of IAM?

07. December 2016 2016 0

Author: Jon Topper
Editors: Bill Weiss, Alfredo Cambera

Identity Concepts

Identity is everywhere, whether we’re talking about your GitHub id, Twitter handle, or email address. A strong notion of identity is important in information systems, particularly where security and compliance is involved, and a good identity system supports access control, trust delegation, and audit trail.

AWS provides a number of services for managing identity, and today we’ll be looking at their main service in this area: IAM – Identity and Access Management.

IAM Concepts

Let’s take a look at the building blocks that IAM provides.

First of all, there’s the root user. This is how you’ll log in when you’ve first set up your AWS account. This identity is permitted to do anything and everything to any resource you create in that account, and – like the unix root user – you should really avoid using it for day to day work.

As well as the root user, IAM supports other users. These are separate identities which will typically be used by the people in your organization. Ideally, you’ll have just one user per person; and only one person will have access to that user’s credentials – sharing usernames and passwords is bad form. Users can have permissions granted to them by the use of policies.

Policies are JSON documents which, when attached to another entity, dictate what those entities are allowed to do.

Just like a unix system, we also have groups. Groups pull together lists of users, and any policies applied to the group are available to the members.

IAM also provides roles. In the standard AWS icon set, an IAM Role is represented as a hard hat. This is fairly appropriate, since other entities can “wear” a role, a little like putting on a hat. You can’t log directly into a role – they can’t have passwords – but users and instances can assume a role, and when they do so, the policies associated with that role dictate what they’re allowed to do.

Finally we have tokens. These are sets of credentials you can hold, either permanent or temporary. If you have a token you can present these to API calls to prove to them who you are.

IAM works across regions, so any IAM entity you create is available everywhere. Unlike other AWS services, IAM itself doesn’t cost anything – though obviously anything created in your account by an IAM user will incur costs in the same way as if you’ve done it yourself.

Basic Example

In a typical example we may have three members of staff: Alice, Bob and Carla. Alice is the person who runs the AWS account, and to stop her using the root account for day to day work, she can create herself an IAM user, and assign it one of the default IAM Policies: AdministratorAccess.

As we said earlier, IAM Policies are JSON documents. The AdministratorAccess policy looks like this:

The Version number here establishes which version of the JSON policy schema we’re using and this will likely be the same across all of your policies. For the purpose of this discussion it can be ignored.

The Statement list is the interesting bit: here, we’re saying that anyone using this policy is permitted to call any Action(the * is a wildcard match), on any Resource. Essentially this holder of this policy has the same level of access as the root account, which is what Alice wants, because she’s in charge.

Bob and Carla are part of Alice’s team. We want them to be able to make changes to most of the AWS account, but we don’t want to let them manipulate users – otherwise they might disable Alice’s account, and she doesn’t want that! We can create a group called PowerUsers to put Bob and Carla in, and assign another default policy to that group, PowerUserAccess, which looks like this:

Here you can see that we’re using a NotAction match in the Statement list. We’re saying that users with this policy are allowed to access all actions that don’t match the iam:* wildcard. When we give this policy to Bob and Carla, they’re no longer able to manipulate users with IAM, either in the console, on the CLI or via API calls.

This, though, presents a problem. Now Bob and Carla can’t make changes to their own users either! They won’t be able to change their passwords, for a start, which isn’t great news.

So, we want to allow PowerUsers to perform certain IAM activities, but only on their own users – we shouldn’t let Bob change Carla’s password. IAM provides us with a way to do that. See, for example, this ManageOwnCredentials policy:

The important part of this policy is the ${aws:username} variable expansion. This is expanded when the policy is evaluated, so when Bob is making calls against the IAM service, that variable is expanded to bob.

There’s a great set of example policies for administering IAM resources in the IAM docs, and these cover a number of other useful scenarios.

Multi-Factor Authentication

You can increase the level of security in your IAM accounts by requiring users to make use of a multi-factor authentication token.

Your password is something that you know. An MFA token adds a possession factor: it’s something that you have. You’re then only granted access to the system when both of these factors are present.

If someone finds out your password, but they don’t have access to your MFA token, they still won’t be able to get into the system.

There are instructions on how to set up MFA tokens in the IAM documentaion. For most types of user, a “virtual token” such as the Google Authenticator app is sufficient.

Once this is set up, we can prevent non-MFA users from accessing certain policies by adding this condition to IAM policy statements:

As an aside, several other services permit the use of MFA tokens (they may refer to it as 2FA) – enabling MFA where available is a good practise to get into. I use it with my Google accounts, with Github, Slack, and Dropbox.

Instance Profiles

If your app needs to write to an S3 bucket, or use DynamoDB, or otherwise make AWS API calls, you may have AWS access credentials hard-coded in your application config. There is a better way!

In the Roles section of the IAM console, you can create a new AWS Service Role, and choose the “Amazon EC2” type. On creation of that role, you can attach policy documents to it, and define what that role is allowed to do.

As a real life example, we host application artefacts as package repositories in an S3 bucket. We want our EC2 instances to be able to install these packages, and so we create a policy which allows our instances read-only access to our S3 bucket.

When we create new EC2 instances, we can attach our new role to it. Code running on the instance can then request temporary tokens associated with the new server role.

These tokens are served by the Instance Metadata Service. They can be used to call actions on AWS resources as dictated by the policies attached to the role.

diagram

The diagram shows the flow of requests. At step 1, the application connects to the instance metadata service with a request to assume a role. In step 2, the metadata service returns a temporary access token back to the application. In step 3, the application connects to S3 using that token.

The official AWS SDKs are all capable of obtaining credentials from the Metadata Service without you needing to worry about it. Refer to the documentation for details.

The benefit of this approach is that if your application is compromised and your AWS tokens leak out, these can only be used for a short amount of time before they’ll expire, reducing the amount of damage that can be caused in this scenario. With hard-coded credentials you’d have to rotate these yourself.

Cross-Account Role Assumption

One other use of roles is really useful if you use multiple AWS accounts. It’s considered best practice to use separate AWS accounts for different environments (eg. live and test). In our consultancy work, we work with a number of customers, who each have four or more accounts, so this is invaluable to us.

In our main account (in this example, account ID 00001), we create a group for our users who are allowed to access customer accounts. We create a policy for that group, AssumeRoleCustomer, that looks like this:

In this example, our customer’s account is ID 00005, and they have a role in that account called ScaleFactoryUser. ThisAssumeRoleCustomer policy permits our users to call sts:AssumeRole to take on the ScaleFactoryUser role in the customer’s account.

sts:AssumeRole is an API call which will return a temporary token for the role specified in the resource, which we can then use in order to behave as that role.

Of course, the other account (00005) also needs a policy to allow this, and so we set up a Trust Relationship Policy:

This policy allows any entity in the 00001 account to call sts:AssumeRole in our account, as long as it is using an MFA token (remember we saw that conditional in the earlier example?).

Having set that up, we can now log into our main account in the web console, click our username in the top right of the console and choose “Switch Role”. By filling in the account number of the other account (0005), and the name of the role we want to assume (ScaleFactoryUser) the web console calls sts:AssumeRole in the background, and uses that to start accessing the customer account.

Role assumption doesn’t have to be cross-account, by the way. You can also allow users to assume roles in the same account – and this can be used to allow unprivileged users occasional access to superuser privileges, in the same way you might use sudo on a unix system.

Federated Identity

When we’re talking about identity, It’s important to know the difference between the two “auth”s: authentication and authorization.

Authentication is used to establish who you are. So, when we use a username and password (and optionally an MFA token) to connect to the web console, that’s authentication at work.

This is distinct from Authorization which is used to establish what you can do. IAM policies control this.

In IAM, these two concepts are separate. It is possible to configure an Identity Provider (IdP) which is external to IAM, and use that for authentication. Users authenticated against the external IdP can then be assigned IAM roles which control the authentication part of the story.

IdPs can be either SAML or using OpenID Connect. Google Apps (or are we calling it G-Suite now?) can be set up as a SAML provider, and I followed this blog post with some success. I can now jump straight from my Google account into my AWS console, taking on a role I’ve called GoogleSSO, without having to give any other credentials.

Wrapping Up

I hope I’ve given you a flavour of some of the things you can do with IAM. If you’re still logging in with the root account, if you’re not using MFA, or if you’re hard-coding credentials in your application config, you should now be armed with the information you need to level up your security practice.

In addition to that, you may benefit from using role assumption, cross-account access, or an external IdP. As a bonus hint, you should also look into CloudTrail logging, so that your Alice can keep an eye on what Bob and Carla are up to!

However you’re spending the rest of this year, I wish you all the best.

About the Author

Jon Topper has been building Linux infrastructure for fifteen years. His UK-based consultancy, The Scale Factory, are a team of DevOps and infrastructure specialists, helping organizations of various sizes design, build, operate and scale their platforms.

About the Editors

Bill is a senior manager at Puppet in the SRE group.  Before his move to Portland to join Puppet he spent six years in Chicago working for Backstop Solutions Group, prior to which he was in New Mexico working for the Department of Energy.  He still loves him some hardware, but is accepting that AWS is pretty rad for some some things.

Alfredo Cambera is a Venezuelan outdoorsman, passionate about DevOps, AWS, automation, Data Visualization, Python and open source technologies. He works as Senior Operations Engineer for a company that offers Mobile Engagement Solutions around the globe.