AWS IAM: Permission Management is as Simple as Donuts. Concepts and Policies

Every project journey with AWS starts from resource creation. Sure, what you want to do is dive deep into exciting serverless, powerful high-availability or even build your brand new rocketship right away. But first, you will definitely come face to face with AWS Identity and Access Management Service.

Anna Korkoshko
GreenM

--

AWS IAM is tightly coupled with every resource creation and should be provisioned wisely to avoid errors or security issues. In this series of articles, we will start from the basics and will gradually move on to subsequent parts.

This article describes basic IAM concepts and IAM policy building components flavored with tasty donuts examples. So go on, maybe stop by the bakery first, and enjoy!

Understanding IAM Components

First of all, let’s imagine you’re a policeman. So you come to the police station and see a bunch of donuts lying on the dinner table. Can you eat them? Are they shared? Or somebody bought them just for you? Or they might be your chief’s donuts and you’d better not even look or sniff in that direction. How do you know?

Right, there should be a donuts policy somewhere. It might be in the Code of Conduct or just in your head. But everyone should know if they are allowed to eat a donut or not.

So what are AWS IAM components from the donut perspective?

An AWS IAM resource is a donut you may eat. Or any other thing you can interact with in the office.

An AWS IAM policy is a document, which declares some permission statements. In a minute, we will see what it can declare and how.

AWS Identities:

An AWS IAM user is just a person like you, your colleague or your boss.

An AWS IAM group is a collection of IAM users united on a permission basis. There can be such groups in a police department as police officers, detectives, crime scene investigators. Or: Majors, Captains, Lieutenants. One IAM user can be a part of several groups.

But here is another identity type — AWS IAM Role. I need a bit more of your imagination at this point.

Let’s assume that you own a donut bakery-shop aside from your fascinating policeman’s job.

There are three roles in your donut shop: a cleaner, chef, and cashier. Also, there is a policeman’s role. To assume the role is to act according to the permissions and restrictions this role entails. So let’s become a chef by assuming the “chef role.” Only a chef can go to the kitchen and create donuts, but they can not sell donuts and talk to customers.

They can’t arrest people as well! So to become a policeman again we need to leave the “chef role” and assume “policeman role.” Wow! Now you have a badge again! Moreover, you can eat some donuts! But you’d still better check those donut policies to know for sure.

So to assume a role, is like wearing a magic chef’s hat and becoming a chef for a while with all the chef’s privileges and prohibitions.

AWS IAM Policy Syntax

So let’s move to the most exciting part — AWS Policies. We already know that there is some document (just a JSON actually) that regulates permission. You can create json policy by yourself or use a visual editor in the AWS Management Console. So let’s look closer.

A policy consists of statements, there can be one or more statements in one policy.

Here is a simple S3 bucket policy example:

{
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Principal":{
"AWS":"arn:aws:iam::111122223333:root"
},
"Action":[
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource":[
"arn:aws:s3:::examplebucket/*"
],
"Condition":{
"IpAddress":{
"aws:SourceIp":"54.240.143.0/24"
}
}
}
]
}
}

Please notice parts starting with “ARN”, which stands for Amazon Resource Name and means a unique identifier for any resources you create in AWS.

Let’s discuss statement components

Effect. You can have two different intentions when you are building your policy: to grant or restrict access to actions/resources. So you should specify Effect — “Allow” or “Deny.”

Principal (NotPrincipal). The entity that is allowed or denied access. E.g. user, role, AWS service. Principals are only required in resource policies (We are going to cover policy types in the next article)

Action (NotAction). Here you declare the type of access that you allow or deny. It consists of two parts: AWS Service and action available for this service.

Assume that we have a “food” service in the office. It allows buying and eating some food. It allows changing the food provider as well.

So the office manager will be allowed to

“Action”:[”food:buy”, “food:changeProvider”]

Then employees would have permissions to

“Action”:[“food:eat”]

Please note the difference between

“Effect”:”Deny” 
“Action”: [“food:eat”]

and

“Effect”:”Allow”
“NotAction”: [“food:eat”]

The first example explicitly denies the action ‘eat’ from the food service. However, the second example ALLOWs all the actions from the service ‘food’ except action ‘eat.’

Resource. The Amazon resource(s) which is a target for an action.

Actually, the resource here is our tasty donut. Now we can combine the resource and the action:

“Action”:[“food:eat”],
“Resource”:”pinkDonut”

This simply means: it is allowed to eat a pinkDonut using food service.

Note, that actions are valid only for specific types of resources. You can’t use action “food:eat” with a “car” resource, but probably there is a service somewhere to drive it.

Condition. The conditions under the defined access is valid.

There are a lot of conditions available for policies. You will need them when you use “but only” phrase when creating a policy. There are some life examples:

I want to allow my employees to eat pink donuts, but only if they are “lieutenants”. (Only they deserve)

I want to allow my employees to eat pink donuts, but only till 2pm. (Seems likely you care about their health)

I want to allow my employees to eat pink donuts, but only if they finished their daily reports. (Encourage fast reports)

Wildcards

You can use wildcards for some elements of the policy. Let’s look at some examples:

"Resource": "arn:aws:s3:::*"

This means the policy will be applied to all resources in s3.

"Resource": "arn:aws:s3:::corporate*"

This policy will be applied to all buckets starting with “corporate.”

"Action": "s3:*"

This policy will provide access to all actions from s3 service.

"Action": "ec2:*KeyPair*"

This policy will provide access to all actions available with KeyPairs: DescribeKeyPairs, CreateKeyPair, DeleteKeyPair, ImportKeyPair.

Please note that you should be extremely careful with wildcards and think twice before you use them. Being specific in policies is a best practice that should be followed to build a secure environment.

Do you really want to give permissions to terminate instances to QA engineers when writing “Action”: “ec2:*”? Do you really want to give anybody access to all s3 resources by writing “Resource”: “arn:aws:s3:::*”? Despite one of the buckets contains some sensitive data? Currently, the ec2 service has more than 350 actions. Are you sure that you know all of them and want to give your developers access to all the actions?

And there is an even more important point. When you use a wildcard, you give access not only to existing actions/resources but also to those which will be added in the future.

So “wildcards” is a very useful feature but please use it responsibly.

A bit of real-world practice

Now, we are going to get our hands dirty and build a simple real-world policy example. Assume, we have an environment with EC2 Instances and S3 buckets. Our task is to build a policy for developers. They should be able to see all instances, stop and start instances with a special dev tag (from the office only) and have access to S3 buckets with dev resources.

First, let’s allow users to “Describe Instances” using ec2 service, which is really simple:

{
"Effect":"Allow",
"Action":"ec2:DescribeInstances",
"Resource":"*"
}

Next, we will need to allow starting and stopping instances, but only if the user accesses AWS from the office IP. Rember? “But only” phrase means using Conditions. Additionally, we are going to use Condition for resource tag to allow interaction only with dev instances.

{
"Effect":"Allow",
"Action":[
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource":"*",
"Condition":{
"StringEquals":{
"ec2:ResourceTag/env":"dev"
},
"IpAddress":{
"aws:SourceIp":"210.75.12.75"
}
}
}

And finally, we want to allow users to list the content of the dev buckets; PutObject and Get Object from them. So we will use actions from the s3 service.

Let’s assume that all our dev buckets’ names start with “devdata” prefix. This is a good place to make good use of wildcards.

{
"Effect":"Allow",
"Action":"s3:ListBucket",
"Resource":"arn:aws:s3:::devdata*"
},
{
"Effect":"Allow",
"Action":[
"s3:PutObject",
"s3:GetObject"
],
"Resource":"arn:aws:s3:::devdata*/*"
}

That’s it. Here you can see the full policy content. Easy, right?

{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":"ec2:DescribeInstances",
"Resource":"*"
},
{
"Effect":"Allow",
"Action":[
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource":"*",
"Condition":{
"StringEquals":{
"ec2:ResourceTag/env":"dev"
},
"IpAddress":{
"aws:SourceIp":"210.75.12.75/16"
}
}
},
{
"Effect":"Allow",
"Action":"s3:ListBucket",
"Resource":"arn:aws:s3:::devdata*"
},
{
"Effect":"Allow",
"Action":[
"s3:PutObject",
"s3:GetObject"
],
"Resource":"arn:aws:s3:::devdata*/*"
}
]
}

Conclusion

As you can see, the AWS IAM basic components are pretty straightforward and intuitive, yet powerful and flexible. You can use hundreds of actions of more than a hundred services with numerous resources.

Fine-grained access management will help you to follow all the security best practices and achieve a convenient environment for your project teams. You can always find more details on policy creation in the IAM User Guide.

In the subsequent article, we will cover different policy types and how they interact. In addition, we will look into the policy evaluation process, obviously from the donuts perspective.

--

--