IAM Basics
Identity and Access Management is all about getting AWS to let you make API call. This means learning about permission Policies, in the form of finicky JSON.
What's a Policy
Policies are JSON documents that describe who can do what. Since everything is denied by default, Policies generally set who is allowed to do a thing (although you can explicitly deny stuff too).
Here's a policy:
1{ 2 "Version": "2012-10-17", 3 "Statement": [ 4 { 5 "Sid": "ReadOnlyS3Example", 6 "Effect": "Allow", 7 "Action": [ 8 "s3:GetObject", 9 "s3:GetObjectVersion",10 "s3:ListBucket"11 ],12 "Resource": [13 "arn:aws:s3:::amzn-s3-demo-bucket/*",14 "arn:aws:s3:::amzn-s3-demo-bucket"15 ]16 }17 ]18}
This will effectively allow read-only access to objects inside of an S3 bucket amzn-s3-demo-bucket
.
Here's some of what's going on there (note how things are singular, not plural!):
- Version: This is boilerplate, just throw this date in there.
- Statement: A list of policy statements, you can have many
- Sid: Arbitrary statement id, useful for leaving notes for yourself. Theoretically does not support dashes, but you can usually use them (certain times that will make them fail, so generally don't use dashes)
- Effect: Allow or Deny
-
Action: A single or list of actions (api calls). Supports wildcards, e.g.
s3:*
- Resource: Items the policy effects, usually the ARN of some AWS resource
S3 policies are interesting because you have to list the bucket itself and objects within the bucket separately. For example, the resource
arn:aws:s3:::amzn-s3-demo-bucket
relates to actions specific to buckets. The resourcearn:aws:s3:::amzn-s3-demo-bucket/*
related to actions specific to the buckets objects.This implies (correctly) that you can make the permissions specific to objects in the bucket, e.g.
arn:aws:s3:::amzn-s3-demo-bucket/path/to/a/dir/or/file.ext
.
Principals
Policies are attached to things, and these things are who can do the actions a policy allows.
The "who" is often called a Principal, and they include (among others) these things:
- IAM Users
- IAM Groups
- Roles
Users
Everyone's favorite thing that AWS doesn't want you to use (we all use it). IAM Users let you create credentials (key + secret key) that can be used anywhere - inside or outside of AWS services.
Whatever Policies are attached to the User are the permissions you get with that key/secret key combo.
These credentials are the things that get leaked all over the internet, usually thanks to careless commits to git. GitHub does a good job of alerting you of this, but you'll also learn about leaked creds when your account gets taken over by crypto miners.
User credentials are static. You should rotate them often. Few of us do this!
Groups
You can put IAM Users into a Group, and then put Policies on the Group itself. Then the users in that group get those permissions. Not much else to say!
Roles
The unassuming Role (that's a pun, but if you don't get it: congratulations) is just, like, a whole thing.
Policies can be attached to Roles, just like Users or Groups. Roles, however, are meant to use used programmatically. By "used", I mean "assumed". In this case, "assuming" a role means to take a role on (e.g. "I'll assume this burden").
Assuming a role grants you the permissions the policies on the role give.
This is the preferred way to gain access to AWS as, under the hood, assuming a Role is a process of gaining temporary (short-lived) credentials.
There is, of course, the matter of allowing a role to be assumed. This is actually Yet Another Policy (more JSON), called a Trust Policy.
Every role has a Trust Policy which states which Principal can assume the role. A Principal can be quite a few things - IAM users, other Roles, entities in other accounts, AWS services, federated identities (fancy OIDC), and more.
The ability to assume a role is thanks to an API called "Simple Token Service". It's essentially a service whose job it is to generate temporary keys. This is why, within a Trust Policy, you'll see an Action allowed such as
sts:AssumeRole
orsts:AssumeRoleWithWebIdentity
.
Here's a Trust Policy that allows the EC2 service in your account permission to assume the Role:
1{ 2 "Version": "2012-10-17", 3 "Statement": [ 4 { 5 "Sid": "EC2InstanceProfile", 6 "Effect": "Allow", 7 "Principal": { 8 "Service": "ec2.amazonaws.com" 9 },10 "Action": "sts:AssumeRole"11 }12 ]13}
Here's some common ways Roles are used:
- Attached to an EC2 instance as part of an Instance Profile (the above Trust Policy is used for that)
- Attached to an Kubernetes
ServiceAccount
(which in turn is associated with some workload running in your EKS cluster). Fun things to google for this include "IRSA" and "Pod Identity". - Attached to a Lambda function
- Assigned to federated users when using SSO to authenticate with AWS
- Granting access to third-parties without having to generate static credentials
All of these things are, under the hood, assuming a Role. With role assumption, AWS SDK's are (when used within AWS services) able to gain permissions to talk to the AWS API automatically. They're setup to search for the temporary credentials created when a Role is assumed.
That's why code like the following Just Works™ when a Role is associated with your compute on EC2, Lambda, whatever:
1use Aws\S3\S3Client;2 3# No other auth config needed!4$s3Client = new S3Client([5 'region' => 'us-east-2',6 'version' => '2006-03-01'7]);8 9$buckets = $s3Client->listBuckets();
Using roles outside of AWS is a bit trickier, as your code likely needs to be made aware of the role assumption process - manually making API calls to assume a role, retrieving the temporary credentials, and then using them.
AWS is complex. Sign up for free, useful lessons like this.