Significance of resource in Resource Based Policy - amazon-web-services

I am trying to understand resource based policy in IAM.
I understand : it is attached to a resource like s3,KMS, secrets manager etc.
My question is what is the significance Resource in a resource based policy.
For example a permission policy for AWS secrets manager(https://aws.amazon.com/premiumsupport/knowledge-center/secrets-manager-resource-policy/)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:*",
"Principal": {"AWS": "arn:aws:iam::123456789999:user/Mary"},
"Resource": "*"
}
]
}
Here the Resource is * or the resource can be the ARN of the secrets manager. (Is there any other value allowed in this case ? ) For S3 I can think of the root bucket or other prefixes.
So my question is what is the use case for Resource here ? Please let me know if I am reading it wrong.
Thanks in advance.

Looking in the User Guide, you can see:
Resource: which secrets they can access. See Secrets Manager resources.
The wildcard character (*) has different meaning depending on what you attach the policy to:
In a policy attached to a secret, * means the policy applies to this secret.
In a policy attached to an identity, * means the policy applies to all resources, including secrets, in the account.
So in the case where it is attached to the secret, it effectively has no meaning that differs from *, but it is when you attach it to an identity that it becomes more useful. Then you can give differing identities different action permissions on various secrets.

Resource is the resource that the policy refers to. It allows for more fine grained control over policies.
Take an example-
You host several DynamoDB tables, each of which have multiple indexes. You want to grant users in group A access to some of the tables, along with their indexes.
You want to give users in group B access to a single table, but none of the indexes.
And you want to give users in group C access to a single table, along with all 3 of its indexes.
When you specify the resource in the policy for group A
"resource": ["arn::<table-a-arn>/","arn::<table-b-arn>/","arn::<table-b-arn>/index/gsi1"]
The resource policy for group B "resource": "arn::<table-c-arn>/"
And for group C "resource": ["arn::<table-a-arn>/","arn::<table-a-arn>/index/*"]
Another use case if for explicit denies. An explicit deny always overrides an implicit allow. If you grant full access to EC2 in an account with a policy with EC2 permissions with "resource": * but there is a single instance that you want to limit access to by the entity to which you are applying the policy you would also add a deny statement to the policy with "resource": <some-super-private-instance>

Related

AWS IAM assuming same role with session tag for tenant isolation

I am working on a serverless app powered by API gateway and AWS lambda. Each lambda has a separate role for least privilege access. For tenant isolation, I am working on ABAC and IAM
Example of the role that provides get object access to s3 bucket having <TenantID> as the prefix.
Role Name: test-role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
],
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
],
// Same role ARN: Ability to assume itself
"Resource": "arn:aws:iam::<aws-account-d>:role/test-role"
}
]
}
I am assuming the same role in lambda but the the session tag as
const credentials = await sts.assumeRole({
RoleSessionName: 'hello-world',
Tags: [{
Key: 'TenantID',
Value: 'tenant-1',
}],
RoleArn: 'arn:aws:iam::<aws-account-d>:role/test-role'
}).promise();
I am trying to achieve ABAC with a single role instead of two(one role with just assuming role permission, another role with actual s3 permission) so that it would be easier to manage the roles and also won't reach the hard limit of 5000.
Is it a good practice to do so, or does this approach has security vulnerability?
It should work, but feels a bit strange to re-use the role like this. It would make more sense to me to have a role for the lambda function, and a role for the s3 access that the lambda function uses (for a total of two roles).
Also make sure that you're not relying on user input for the TenantID value in your code, because it could be abused to access another tenant's objects.
TLDR: I would not advise you to do this.
Ability to assume itself
I think there is some confusion here. The JSON document is a policy, not a role. A policy in AWS is a security statement of who has access to what under what conditions. A role is just an abstraction of a "who".
As far as I understand the question, you don't need two roles to do what you need to do. But you will likely need two policies.
There are two types of policies in AWS, of interest to this question: Identity based policies and Resource Based policies:
Identity-based policies are attached to some principal, which could be a role.
Resource-based policies are attached to a resource - which also could be a role!
A common use case of roles & policies is for permission delegation. In this case, we have:
A Role, that other principals can assume, maybe temporarily
A trust policy, which controls who can assume the role, under what conditions, and what actions they can take in assuming it. The trust policy is a special case of a resource policy, where the resource is the role itself.
A permissions policy, which is granted to anyone who assumes the role. This is a special case of an identity policy, which is granted based on the assumption of a role.
Key point: both policies are associated to the same role. There is one role, two policies.
Now, let's take a look at your policy. Clearly, it's trying to be two things at once: both a permissions policy and a trust policy for the role in question.
This part of it is trying to be the trust policy:
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
],
// Same role ARN: Ability to assume itself
"Resource": "arn:aws:iam::<aws-account-d>:role/test-role"
}
Since the "Principal" section is missing, looks like it's allowing anyone to assume this role. Which looks a bit dodgy to me, especially since one of your stated goals was "least privilege access".
This part is trying to be the permissions policy:
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
],
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
This doesn't need a "Principal" section, because it's an identity policy.
Presumably you're resuing that policy as both the trust policy and the permissions policy for the given role. Seems like you want to avoid hitting the policy (not role) maximum quota limit of 5000 defined here:
Customer managed policies in an AWS account
Even if somehow it worked, it doesn't make sense and I wouldn't do it. For example, think about the trust policy. The trust policy is supposed to be a resource-based policy attached to the role. The role is the resource. So specifying a "Resource" in the policy doesn't make sense, like so:
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
Even worse is the inclusion of this in the trust policy:
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
],
"Resource": "arn:aws:s3:::test-bucket/${aws:PrincipalTag/TenantID}/*"
},
What does that even mean?
Perhaps I'm misunderstanding the question, but from what I understand my advice would be:
Keep your one role - that's OK
Create two separate policies: a trust policy & a permissions policy
Consider adding a "Principal" element to the trust policy
Attach the trust & permissions policies to the role appropriately
Explore other avenues to avoid exceeding the 5000 policy limit

AWS IAM: How does the condition context key "aws:RequestTag" work?

I have an EC2 instance with a role attached to it. My goal is to provide full access to AWS service (Lambda for example) but only on certain resources (Tag based). I found that aws:RequestTag was the way to do it.
Below is the IAM policy attached to the role.
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1614664562621",
"Action": "lambda:*",
"Effect": "Allow",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:ResourceTag/app": "prod"
}
}
}
]
}
I added the tags app:prod on the required lambda functions but however when I try to list the lambda I get an AccessDeniedException error. Below is the error message
An error occurred (AccessDeniedException) when calling the
ListFunctions operation: User:
arn:aws:sts::123456789:assumed-role/iam-role-name/i-01abcd456abcd is
not authorized to perform: lambda:ListFunctions on resource: *
How to make the aws:RequestTag work? Where am I going wrong?
Similar question below: (That solution didn't work for me)
aws:RequestTag on s3 bucket is not working (while assuming a role)
You probably want to use aws:ResourceTag instead in your condition and tag the resources (i.e. Lambda functions) that this policy should permit access to.
aws:RequestTag is used to control which tags can be carried in an AWS API call such as for adding/editing/removing a resource tag on a resource or adding session tags on a session (via an sts:TagSession call). They are not meant to protect access to resources having a specific tag.
Also, adding the tag on your role does not mean that any caller identity (i.e. assumed session role) will then have this tag as a request/session tag. And consequently, it will not control any authorization/access to resources with that tag. The IAM role that you tagged simply is another AWS resource with a resource tag applied to it now.
Additionally, you couldn't even control session tags when EC2 assumes your role in the EC2 instance, so you cannot control session/request tags for your EC2 instance.
EDIT: In your particular example with lambda:ListFunctions, though, which is not a resource-specific action, you cannot control/filter the list by Lambda functions having a specific resource tag allowed by the policy of the API caller.
When working with multiple environments/stages, having multiple AWS accounts (one per environment/stage) is actually best practice. You can then even use AWS Organizations for consolidated billing, etc. if you don't already use multiple AWS accounts.

Restricting a Role's S3 Write Privileges

I would like to restrict my role from writing to unauthorized buckets in different accounts. For example, I will have role A in account A. S3 bucket B is created in account B and has a bucket policy allowing role A to write into it. I need a policy on role A/account A to prevent role A from being able to write into bucket B.
Is this possible?
Probably the easiest (from an IAM Policy perspective!) way to achieve what you are looking for, while minimizing the risk of overlooking something and introducing a potential security problem, is to use Access Points.
You can create Access Points and associate them with your buckets. Then, instead of trying to interact with the bucket directly, you interact with the Access Point.
The reason this can help you is that there's an IAM Policy Condition Key available to test the Account ID that owns an Access Point. What you need to do, then, is simply add a statement to your IAM Role's Policy that will "Effect": "Deny" all S3 actions, on all resources, when the request matches a condition that tests "StringNotEquals": { "s3:DataAccessPointAccount": "YOUR_ACCT_NUMBER" }.
Note that you'll not be able to access any S3 resources without going through an Access Point. So this will increase your initial setup complexity (and the complexity of creating new buckets, since now you'll also need to create and associate an access point). It will also become more complex to interact with S3, since you'll now always need to go through the Access Point.
Those are trade-offs you'll need to accept if you want to implement a solution like this. But it'll achieve your goal: it'll be impossible for this IAM Role to access S3 buckets outside of your account.
Here's what the Policy would look like (tailor it to your specific needs):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "denyWithoutAccessPoint",
"Effect": "Deny",
"Action": "s3:*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"s3:DataAccessPointAccount": "YOUR_AWS_ACCT_ID"
}
}
}
]
}
Keep in mind that you'll also need to Allow any operations that you need.
If this trade-off (more complexity interacting with S3 for a completely straightforward IAM Policy) doesn't work for you, you'll need something different.
Keep in mind that there's always a trade-off.
Alternative 1
One alternative possibility is what #Marcin described: explicitly deny access to these buckets in the other accounts.
However, the trade-off here is that you'll never know what are all the S3 Buckets, owned by other AWS accounts, that granted access to your IAM Role.
So you can only deny access to the buckets you know about.
In a threat model in which the attacker wants to exfiltrate data from your account, they could create a new bucket that you don't know about, grant access to the IAM Role through a Bucket Policy on that new bucket, and then somehow make to role write into that newly created bucket.
Benefit: no changes to how your applications use S3.
Disadvantage: possible attack scenario since you can't know the entire list of buckets from other accounts that allow access to your role (i.e., you'd be blocking access "reactively", that is, only after something bad could have already happened).
Alternative 2
Another alternative is for you to instead create an IAM Policy that explicitly denies access to all buckets NOT enumerated in the policy.
To implement this "negated list", you use NotResource, rather than the more common Resource policy element.
Here's what the policy would look like:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "denyWithoutAccessPoint",
"Effect": "Deny",
"Action": "s3:*",
"NotResource": [
"arn:aws:s3:::my-bucket-1",
"arn:aws:s3:::my-bucket-1/*",
"arn:aws:s3:::my-bucket-2",
"arn:aws:s3:::my-bucket-2/*"
]
}
]
}
Again, like in the other sample policy in this answer, remember that you'll still need to explicitly allow actions.
Benefit: you don't need to change the way you interact with S3, and you don't have to know the names of all buckets in other accounts that allow access to your IAM Role.
Disadvantage: you need to maintain this growing list of buckets. Also, keep in mind that there's a maximum policy size that you may eventually hit, making this solution limited in scale (although it can grow quite a lot).
By default buckets are private. But if you already have a bucket policy allowing a role in Account A to write to it and don't want to check the policy, you could add
an explicit deny to the role.
The deny would prohibit s3::PutObject on the bucket and its objects. This works because:
an explicit deny in any of these policies overrides the allow.
En example of such a policy which could be added to the role in Account A is following:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "deny-puts-to-bucket-in-acc-b",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": [
"arn:aws:s3:::<bucket-from-account-B>/*",
]
}
]
}
This only denies PutObject. You may consider other actions as well such as PutObjectAcl or actions on the bucket itself and many more.
Nevertheless, the above policy should be a good start to tailor it to your specific requirements.

Am I allowed to connect to arbitrary RDS DB instances if given the RDS DbiResourceId?

I am checking the steps of setting up IAM auth in RDS: https://aws.amazon.com/premiumsupport/knowledge-center/users-connect-rds-iam/ And one of the steps is to attach the IAM role with proper permission: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds-db:connect"
],
"Resource": [
"arn:aws:rds-db:us-east-2:1234567890:dbuser:db-ABCDEFGHIJKL01234/db_user"
]
}
]
}
The resource follows this format:
arn:aws:rds-db:region:account-id:dbuser:DbiResourceId/db-user-name
If I understand correctly, as long as I know someone's account-id, DbiResourceId and db-user-name (or maybe db-user-name as I can use wildcard?), then I am able to connect to that DB instance, right?
This sounds insecure. Did I miss anything?
No this would not be possible. The only want to interact with this resource would be to assume a role in the target account.
You can use an IAM role to allow someone (a trusted principal) in a different account to access resources in your account. Roles are the primary way to grant cross-account access. However, with some AWS services, you can attach a policy directly to a resource (instead of using a role as a proxy). To learn the difference between roles and resource-based policies for cross-account access, see How IAM Roles Differ from Resource-based Policies in the IAM User Guide

What is the purpose of 'resource' in an AWS resource policy?

As per title, what is the purpose of having the resource field when defining a resource policy when the resource policy is already going to be applied to a particular resource.
For example, in this aws tutorial, the following policy is defined an attached to a queue. What is the purpose of the resource field?
{
"Version": "2008-10-17",
"Id": "example-ID",
"Statement": [
{
"Sid": "example-statement-ID",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"SQS:SendMessage"
],
"Resource": "arn:aws:sqs:REGION:ACCOUNT-ID:QUEUENAMEHERE",
"Condition": {
"ArnLike": { "aws:SourceArn": "arn:aws:s3:*:*:bucket-name" }
}
}
]
}
S3 is a good example of where you need to include the resource statement in the policy. Let's say you want to have a upload location on S3 bucket.
{
"Version":"2012-10-17",
"Statement":[
{
"Sid":"Upload",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:PutObject"],
"Resource":["arn:aws:s3:::examplebucket/uploads/*"]
}
]
}
In these cases you really don't want to default the Resource to the bucket as it could accidentally cause global access. It is better to make sure the user clearly understands what access is being allowed or denied.
But why make it required for resource policies where it isn't need like SQS? For this let's dive into how resource policies are used.
You can grant access to a resources 2 ways:
Identity based policies for IAM principals (users and roles).
Resource based policies
The important part to understand is how are resource polices used? Resource policies are actually used by IAM in the policy evaluation logic for authorization. To put it another way, resources are not responsible for the actual authorization that is left to IAM (Identity and Access Management).
Since IAM requires that every policy statement have a Resource or NotResource this means the service would need to add the resource when sending it to IAM if it was missing. So let us look at the implications from a design perspective of having the service add the resource if it is missing.
The service no longer would need to just verify the policy is correct.
If the resource is missing from the statement the service would need to update the policy before sending it to IAM.
There is now the potential for two different versions of a resource policy. The one the user created for editing and the one sent to IAM.
It increases the potential for user error and accidentally opening up access by attaching a policy to the wrong resource. If we modify the policy statement in the question drop the resource and condition statement we have a pretty open policy. This could easily be attached to the wrong resource especially from the CLI or terraform.
{
"Sid": "example-statement-ID",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"*"
]
}
Note I answered this from a general design perspective based on my understanding of how AWS implements access management. How AWS implemented the system might be a little different but I doubt it because policy evaluation logic really needs to be optimized for performance so it's better do to that in in one service, IAM, instead of in each service.
Hope that helps.
Extra reading if you are interested in the details of the Policy Evaluation Logic.
You can deny access 6 ways:
Identity Policy
Resource policies
Organizational Polices if your account is part of an organization
IAM permission boundaries if set
Session Assumed Policy if used
Implicitly if there was no allow policy
Here is the complete IAM policy evaluation logic workflow.
There is a Policy as you defined.
Policy applied resource : A, I don't know where you will apply this.
The resource in the policy : B, arn:aws:sqs:REGION:ACCOUNT-ID:QUEUENAMEHERE
Once you apply the polity to some service like ec2 instance that is A, then the instance only can do SQS:SendMessage through the resource B. A and B are totally different.
If you want to restrict the permission for the resource A that shouldn't access to other resources but can only access to the defined resources, then you have to define the resource such as B in the policy.
Your policy is only valid for that resource B and this is not the resource what you applied A.