A quick overview of AWS principals, identity-based policies, and resource-based policies
Permissions in multi-account AWS environments
One of the more frequent hurdles I watch my team run into when they first learn AWS is that AWS has two primary ways to assign the same privileges to some of its resources. In its documentation, AWS describes the difference between identity-based policies which affect IAM Principals, and resource-based policies that affect AWS resources.
The model of permissions associated with identity-based policies is often referred to as RBAC or (Role-based Access Control).
Consider an S3 bucket as a quick example. I can either define an identity-based policy and attach it to an IAM principal, such as a role or user directly or via a group:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadExampleBucketAndObjects",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket/*",
"arn:aws:s3:::example-bucket"
]
}
]
}
When you attach the above policy is a principal in your account, that principal is able to access objects in the s3 bucket named example-bucket
if that bucket exists in your AWS account.
Note the above caveat: this policy grants access if the bucket exists in your AWS account. If the bucket is in another AWS account, this policy alone is not enough to grant access. I’ll get back to this later.
There is, however, another way to grant the same permissions. Let’s assume you have the following list of AWS principal arns, one a user, and one a role, that you wish to grant read access to the s3 bucket:
[
"arn:aws:iam::123456789012:user/malfoy",
"arn:aws:iam::123456789012:role/deatheater"
]
An alternative strategy to granting access to the bucket and objects in question is to create an s3 bucket policy, or a resource-based policy attached to the s3 bucket itself. This resource-based policy has an extra Principal
key in each statement of its json document that distinguishes it from an identity-based policy.
A resource-based S3 bucket policy that provides equivalent permissions to the above identity-based policy might look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MalfoyAndDeatheaterReadBucketAndObjects",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::123456789012:user/malfoy",
"arn:aws:iam::123456789012:role/deatheater"
],
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket/*",
"arn:aws:s3:::example-bucket"
]
}
]
}
You might notice that the principal arns both share an aws account number 123456789012
. This s3 bucket will be accessible by the aforementioned principals whether or not example-bucket
lives in the same aws account as the principals: 123456789012
.
This highlights a key difference between resource-based policies and identity-based policies.
Identity-based policies cannot expose your resources in your AWS account to principals that exist outside of your AWS account.
The contrapositive is true too:
If you want to grant a principal outside of your AWS account access to your AWS account, you must use a resource-based policy.
This is true even for service principals like lambda.amazonaws.com
. You will notice that whenever you define a Lambda execution role, or an EC2 instance profile, or an ECS task role, or any other role that is assumed by an AWS service, the role is created in your AWS account. The “Assume Role Policy” is effectively a resource-based policy, that is attached to your role. This resource-based policy shares your role with third parties outside of your organization.
But, I hear you ask, what happens if you want to give my AWS account’s IAM administrator the ability to determine who in my organization should have access to your s3 bucket objects? Do you have to keep on modifying the principals in your resource policy so that I can allow different users and roles in my aws account to access your s3 bucket? That sounds rather inconvenient.
That’s where the special IAM AWS account principal comes into play. You can delegate s3 object access to my AWS account by specifying an iam account arn in the Principal block of your resource-based policy as follows:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ShareAccountReadBucketAndObjects",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::123456789012:root"
],
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket/*",
"arn:aws:s3:::example-bucket"
]
}
]
}
This policy does not let all Principals in my AWS account access the objects in your s3 bucket. Instead it delegates the capability to allocate permissions to your S3 bucket to my IAM administrator. After this resource-based policy is defined on your bucket, I would be able to create an identity-based policy identical to the one above:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadExampleBucketAndObjects",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket/*",
"arn:aws:s3:::example-bucket"
]
}
]
}
Now, even though example-bucket
lives in your AWS account, when my IAM administrator attaches this identity-based policy to user malfoy
and role deatheater
, those principals will be able to access objects inside your bucket.
Note that no s3 bucket policy was necessary when your IAM administrator granted permission to your s3 bucket via an iam-based policy. There is, however, an instance in which you must define a resource-based policy to allow your own IAM administrator to grant access to resources inside your AWS account.
You can only define KMS key permissions with identity-based policies if there is an explicit resource-based key policy that grants your IAM account access to delegate these permissions. By default, a policy resembling the below is added to all KMS keys created in the console to allow identity-based policies:
{
"Sid": "Enable IAM policies",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "kms:*",
"Resource": "*"
}
What to use?
I will leave you with some prescriptive guidance that I generally use to recommend when to use resource-based policies vs identity-based policies to my team at Foresight.
Always use identity-based policies unless you need to grant permissions cross account or make a resource public. This makes it easier to understand what a particular principal can do without worrying that additional privileges have been granted to them by resource-policies inadvertently.
I might be persuaded to expand this blanket rule. Do you have a use-case or counter argument as to why resource-based policies might be better? I’d be interested in hearing your thoughts.
Where it says:
"The “Assume Role Policy” is effectively a resource-based role, that is attached to your role."
Do you mean to say ""The “Assume Role Policy” is effectively a resource-based POLICY,"?