AWS IAM: Policies 101

Hamzah Abdulla
12 min readJul 15, 2020

--

OKOKOK… let’s not waste any time and dive right into the real reason your clicked on this article, you wanna know what my second favourite dinosaur is: it IIIISSSSS *drumroll please* … I have to do my own drumroll sadly… A PTERODACTYL! (honestly I had no idea it was spelt with a P! Wow you learn something new everyday huh 🤔 ) I always imagine they are just screeching and screaming all the time for no good reason and I wholly resonate with that.

Hi everybody!! On this epic adventure we shall delve into the mysterious art of policy document creation and uncover the secrets that will allow us to master the dark arts. Let us begin.

The Policy breakdown

In the first part of our quest we will dissect and understand what a Policy is made of (if you’re curious I’m made up of 80% ice cream and about 10% of poor maths skills) and some of the reasoning behind why things are the way they are. We begin with the Policy shown below, captioned frog1.

frog1 — simple Identity based Policy

But first, a quick reminder:

  • An IAM identity is a User, Role or Group
  • Policies can be attached to IAM Identities
  • Policies are a list of permissions and can be found in JSON format
  • Policies use the Effect + PARC attributes

In frog1 we have a single S3 based action that is being performed on an S3 bucket: ListBucket.

When approaching a policy you want to look for 5 key attributes: Effect, Principal, Action, Resource and Condition (or Effect + PARC for short). You’ll notice immediately, there is no Principal mentioned anywhere in frog1, nor any Condition field. SURPRISE! I LIED! No lol I didn’t lie, the Principal is missing because frog1 is an Identity based policy. I know what you’re thinking, clever you! YES! Identity, you’ve seen this word before when we were re-remembering IAM identities, exactly!

This means frog1 is a Policy that is going to be assigned to an IAM identity (Role or User or Group) and whatever Resource that Role/User is assigned to will be assumed to be the Principal, saves us writing anything out which is always good news, so we don’t need to include the Principal here.

And why no Conditions attribute? Well that’s just because it’s an optional field and we chose not to include it here. Now that we have the weird bits done, let’s begin the dissection *puts on surgical gear*

Version

We start off with the Version, this is a mandatory field and has 2 possible values you could assign to it: 2012–10–17 and 2008–10–17. My birthday… ok no it’s not, but they are important to remember. The dates tell AWS what version of the policy document you’re using, changing the version will mean you have access to a different set of functionality. Functionality like Policy variables, for example, are only available in the latest version so please make sure you’re using the latest one. W

Note: when it comes to Policies, and really AWS as a whole, you have 2 sides, there’s the creator of the policy, which is you, and the executor of the policy, which is AWS. You define rules in your policy and then there is a trust that AWS will make sure these rules that you have defined are adhered to.

Statement

Next up we haaaavvveee the Statement block, this is the main body of the policy document and also a mandatory field, no statement block means no permissions.

In here you’ll have your list of policy statements. When you have many Statements it is important to remember that each one has information for a single permission. The Statement block is also a JSON array, which is why you have the square brackets [ ] surrounding your statement(s).

Recognising a JSON list

When you have multiple statements in a single policy, say you have one for S3 permissions and another for EC2 permissions and you're trying to perform an action on S3, then it’s safe to say your EC2 permissions aren’t going to be evaluated against. However, say you have two statements, both of which have permissions relating to S3 and you are performing an action on S3… well, well, well. Got yourself in a pickle haven’t you? haha not really, the way this works is AWS looks at your two S3 permissions, evaluates your actions against the two permissions, and if there is an ALLOW or DENY anywhere, AWS will sniff it out. Three key points to always remember:

  • Everything is denied by default (implicit DENY) denial is not just a river in Egypt 😏
  • One ALLOW is all that is needed for a valid permission
  • However, an explicit DENY overrules any explicit ALLOW. I will repeat, an EXPLICIT DENY overrules an EXPLICIT ALLOW. So you could have 1000 statements that allow s3:Listbucket, but one statement denying it and it’s all over for you. Game Over 👌

Lastly, it’s always a good idea to break up your policy documents by resource types, much like your eggs, you should place your AWS service permissions in different baskets (policy documents). S3 in one bucket, EC2 in another, something like that. And don’t worry, the evaluation matching still happens across policy documents.

Best practise — separate your policies by resource type

Note: you can have up to 10 Policies attached to an IAM Role or an IAM User. This is a soft limit on AWS, however, you can ask for this to be increased and if you ask nicely it goes up to 20 per Role or User. Even though it is an auto-approved no hassle increase, you have to ask as 10 is the default.

Sid

No, Sid is not the infamous sloth from the Ice Age movies… At least, I don’t think so 🤔 no haha this stands for Statement ID and it is an optional attribute to help you differentiate between the many statements you may have by labelling them. If you do include this, be mindful as it must be unique within that policy document. And also you can’t receive particular statements using the Sid via an API call, so really I guess it isn’t too far from the likeness of the sloth from Ice Age, maybe this is an apt metaphor for something that doesn’t do a whole lot, just looks kinda cute 🤷‍♂️

And now it’s time for Effect + PARC.

Effect

Starting with Effect, this is the mandatory explicit ALLOW or DENY in every policy statement. It plays a huge role in policy logic and working out exactly what you want or, more importantly don’t want to allow. An explicit Deny always beats an explicit Allow. Say it 500 times and never ever forget it. This is your new mantra… an update from the last mantra I gave you… the mantra train is a rolling one for me 😂

Action

Lights, Camera, ACTION! This is another mandatory field, and every policy document must have an Action attribute. For the Action all you need to note is that every service in AWS has its own set of actions that can be performed against them. And they come in the form: [service-namespace]:[action]. So, for example, you would have: s3:GetObject or ec2:StartInstances.

Now an action wouldn’t mean anything if we didn’t have the thing the action would be performed on: Resource!!! woooooo! Okok, I’m cutting my coffee off, that’s too much hype.

Resource

Resource is again a mandatory field, you need to tell the policy what it’s aiming at right? That can be singular or plural, we can have a single resource or a list of resources against which we can perform actions. When we specify a resource we use something called an ARN (in my head, which is a pretty strange place, I always say this like a pirate — ARRRRRRNNNN looool) ARN stands for Amazon Resource Name. I know, surprise surprise who would’ve guessed?

An example of what an ARN looks like here is an example of an ARN for an S3 bucket: “arn:aws:s3:::my_corporate_bucket/*”

This can be used in a policy where you are giving access to all the contents within a specific S3 bucket (in this case the super secret “my_corporate_bucket”). ARN’s are generally found on the service pages in the AWS console. You probably also noticed the asterisk at the end of the ARN above (*), this is called wildcard character, it represents any combination of characters (1+ characters). Which means one of more characters can be replaced with this *.

When you put a wildcard character after the “my_corporate_bucket/“ path, this means the anything the comes after the “/” is a valid resource target. This way you don’t have to individually give access to every item in the S3 bucket. Say you had two items in my_corporate_bucket, called item1 and item2, they would reside under my_corporate_bucket/item1 and my_corporate_bucket/item2 respectively. But my_corporate_bucket/* means you have both covered, hey ho prest-o, shorter policy document.

Wildcards explanation

To add multiple resources, throw a cool comma in between your ARN’s and go crazy with as many as the policy document character limit will allow (of course only if you need them lol don’t go too mad).

Note: the aim of a lot of what AWS tries to do by giving you all these functionalities is to help you write cleaner and shorter Policy documents. Case in point, the wildcard (*). (also includes stuff like NotActions and policy variables but I’m getting ahead of myself)

Condition

And finally we have the Conditions field, well fake finally, we still have the Principal field to discuss.

Conditions are awesome. That is all I want to say on that subject… ok no but seriously they really are, this is how you make your policies really YOUR policies. For you and no one else. Where the Resource attribute is great for helping you target an AWS service, conditions help you further narrow down those permissions even within the service. Do you only want to allow access to lambdas that are in the eu-west-1 region? Or an EC2 with a specific tag? Or from a User called: Jeff Bezos? These are all valid conditions and a billionaire thrown in at the end to help me make my point.

Conditions attribute elements

The Conditions attribute is also optional, obvious from the fact that it’s missing from my frog1 policy 😂 They are made up of an operator, key and value. The condition-operator is your regular operand you learnt from maths class, your equals, less than and greater than operator, it’s basically saying the value that you’re defining here in the Condition block in the policy should be equal to or less than or whatever relationship to the one you are evaluating against in the request (request explanation coming right up 😊 ).

Your condition-key, well you gotta know what you’re comparing against right? If the request has an ID field with a value of 10 (ID=10). And your policy has a condition of equal to 10. What equals 10? The ID? Some other field that could equal 10 too? Nope, we need to be specific. We’re saying hey, condition = “I need the ID field to be equal to 10”. Your policy condition right here is evaluated against the request, with the condition-key, condition-operator and condition-value respectively.

Conditions attribute example

You have two types of keys: global keys denoted with the aws prefix (eg aws:SourceIp) and the Service specific key prefixed with eh service name (eg ec2:InstanceType).

The way to think about it is like this, a person or app is performing an action on AWS either via the console, the API or the AWS CLI, this action is performed by submitting a request to AWS. Remember our model we explored earlier, where we talked about creating policy documents, writing out our rules and then trusting AWS to implement and carry them out, well this is where that relationship comes back into play. When we interact with AWS we are ‘requesting’ for them to perform actions on our behalf. But to do so they gotta be sure we are authorised. How do they do this?

When we ask AWS to do anything we are submitting a request that has a bunch of data including the requestor (Principle), the Resource, the Action, details about the resource (eg EC2 instance ID or tags), environment data like the current date or IP address etc. and this request information is gathered and forms what is called a request context.

So when mere mortals such as you and I define conditions in our policies, the condition-key and condition-value we specified are evaluated against the request context. AWS tries to do a match between the two. So if, for example, we used the condition block above, if the source IP of the request we made wasn’t any of the ones stated, we will get a nice big beautiful red box telling us we don’t have the right permissions to do whatever it is we’re trying 😊

Speaking of not meeting the conditions set, let’s quickly cover conditions evaluation logic. From the picture above we see 3 conditions stated, each with a single key and a single value except for the last one that has 2 IP range values. The way AWS will evaluate this is by using a logical AND between the 3 condition statements, meaning they must ALL be true for an overall TRUE for the statement, and using a logical OR between the multiple values for a single key, meaning only one of the IP values needs to be present for the one condition statement to be TRUE. If you have a closer look at the operators being used here you’ll also notice you are checking for a range of dates, this is a common practise in policy conditions and really helpful to make sure you are dealing with up to date request information.

Conditions block evaluation logic summary

I could go on and on and ooooooonnnnnnn about conditions, even more than I already have haha but I will save the exciting stuff for next time 😉

Principal

Finally, The Principal, this is the WHO. Not the owl sound WHO but the doer, the actor, the Leonardo Di Caprio of AWS entities. But unlike the brilliant Leo, the principal entity can only be used in certain roles. The most common place you’ll find the Principal used is in Resource based policies. These are policies that are applied to a Resource, most commonly found on S3 Buckets in the form of a Bucket Policy. Resource Policies differ from Identity policies in three main ways:

  1. Identity based policies are applies to IAM Identities (Roles, Users, Groups) where Resource based are applied to Resources like S3, SQS, KMS Customer Managed Keys
  2. Resource based policies have Principals defined whereas Identity based don’t
  3. Resource based policies tell you what can access them, whereas Identity based you say what they can access

You define the Principal here because you are saying that this Resource, S3 for example, can be access by an AWS entity with this ARN. This is the perfect way to clearly identify an identity using the ARN (*pirate voice* ARRRRNNN — amazon resource name) and can be from Specific AWS accounts, IAM Users to other AWS services and more!

The end

And that, my dear, honoured, prestigious readers, is where our adventure for today leaves us. With a lot to ponder about the realm of IAM policies and even more to look forward to. So much to look forward to that I could only touch on today but oh my oh my we have a lot of exciting things coming up 😊 So I hope you’ve had a great time today and learnt a whole lotta cool Policy stuff to impress your friends, family and pets. There loads more to come, but until then, PEACE OUT, STAY SAFE and WASH THOSE HANDS ✌️

--

--

Hamzah Abdulla
Hamzah Abdulla

Written by Hamzah Abdulla

The thoughts expressed on this platform are wholly my own and in no way reflect those of my employer

No responses yet