Permissions management is a ubiquitous challenge in software. Knowledge and experience in this area is generally useful for anyone building complex web applications, including front-end engineers.
Recently, I’ve been exploring AWS’ Identity and Access Management service (IAM) service and taking the time to understand the different ways granular permissions can be set up for multiple agents (e.g. human users, web services) in the same system. This blog post briefly summarizes some of my learnings thus far and attempts to distill them using layman terms as much as possible.
Definitions
To start, let’s discuss the “I” in IAM. There are three types of identities to be aware of:
User
A single AWS account can have multiple users. Here, the term user refers to an internal user accessing your AWS account — you can think of it as someone on your team. This is not the same, as say, the user (or customer) of the web application you’re building.
For example, you might need to set up some serverless instances to compute data from your customers, a few S3 buckets to host some front-end assets, and use AWS Billing to manage your expenses. Naturally, you’d probably want to set up multiple users with varied permissions. If you have dedicated person overseeing your AWS expenditures, you’d set up a separate user for them which has permissions limited to reviewing your AWS Billing dashboards.
User group
A user group is simply a collection of users. User groups can have permissions applied to them as well.
To use the example from earlier: Imagine if you had a team responsible for overseeing your AWS expenditures, instead of just one individual. You could create a “Billing” user group and assign the group permissions that are scoped specifically to the AWS Billing service.
Roles
Roles can thought of as a special type of identity that can be assumed by users and services. Like users and user groups, every role is also tied with its own set of permissions.
You can think of assuming a role as being analogous to putting on a hat — a hat which grants whoever is wearing it the permissions associated with it.
A core feature of IAM roles is that they can only be assumed temporarily. A user assuming a role, for instance, would have to re-assume this same role again after some time has passed. This is managed by issuing short-term credential to the user every time they assume the role.
It would obviously be problematic if anyone could assume any given role, because this would make any permissioning mechanism meaningless. Hence, roles are also defined with trust policies that dictate who can assume the role and who cannot.
Access Management
Now, let’s move on to the “AM” piece in IAM.
Access management is chiefly regulated using policies.
A policy describes a set of permissions in a given system. For example, let’s suppose you have an AWS account that contains a few private S3 buckets. You want a developer in your team, Mary, to have full read access to all buckets, so you can attach the following policy to Mary’s account:
// AmazonS3ReadyOnlyAccess
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*",
"s3-object-lambda:Get*",
"s3-object-lambda:List*"
],
"Resource": "*"
}
]
}
Policies such as the one described above are evaluated whenever someone attempts to make a view data or make changes to any AWS service.
The best resource I’ve found so far for explaining how all of these policies are evaluated is this section on IAM’s policy evaluation logic. It’s slightly tucked away in the official documentation but it’s also a superbly handy reference, thus I strongly recommend bookmarking it.
Identity-based Policies versus Resource-based Policies
The above policy can be described as an identity-based policy, because we are attaching it to an identity which, in this case, would be the user Mary.
We can contrast this against resource-based policies, which are attached to specific resources. For example, if you now wanted to grant Mary complete control (i.e. including write access) a specific S3 bucket, you could write:
// Custom Policy
{
"Id": "Policy1653970516081",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1653970513563",
"Action": "s3:*",
"Effect": "Allow",
"Resource": "arn:aws:s3:::<BUCKET_ID>",
"Principal": {
"AWS": [
"arn:aws:iam::<ACCOUNT_ID>:user/Mary"
]
}
}
]
}
What’s noteworthy about resource-based policies is that they have a Principal
field, which describes the entity this resource is defining permissions for.
IAM principals are not entirely the same as IAM identities, even if there is some overlap. Principal types include IAM users, IAM roles, AWS services, and many more. Check out this section in the docs to view the full list of Principals and how they can be used in policy statements.
The label “resource-based policy” may cause some cognitive dissonance, since the Principal
field alludes to some kind of “identity”. I’ve found it helpful to frame things this way:
- Identity-based policies are primarily meant to be managed, in the sense that they are applied after they are defined. When we write them, we only need to declare the resource and the action involved, because we’ll already be attaching them to identities later as part of the management process. Inline (i.e. one-off, “unmanaged”) identity-based policies do exist, but those are usually exceptional and not recommended.
- Resource-based policies are purely inline, so we want to define all the actors up front. We don’t just care about the resource — we also care about the entity that is accessing the resource (i.e. the principal), so we’ll want to frontload this information at the point of writing.
The Universe of IAM
We’ve covered the basic concepts, and there’s plenty more exciting things to discuss in the domain of access management, including:
- How we can define permissions across multiple AWS accounts (i.e. cross-account access).
- How we can add an additional layer of “permissions fencing” around certain identities by using permissions boundaries.
- How we can similarly set up a permissions ceiling for AWS organizations in a multi-account, multi-organization scenario using service control policies.
I’ve studied some of these topics and I believe they’re incredibly relevant to anyone looking to contribute at scale. That said, it’s worth mentioning that they are also not always going to be immediately applicable to front-end development use cases.
Ultimately, this exploration of IAM needs to be done in the context of authorization in front-end web applications. How do we define permissions policies for customers who, for instance, log into a website via Google? I will tackle this question in a subsequent blog post, in which I’ll dive deeper into the realm of IAM roles and how they can be assumed by such external users.