I have User with full access to an S3 Bucket my-bucket
. I want to assume a role with that user to restrict access to ONLY a specific folder (labeled by the role name) for the duration of that session. There will be multiple roles/folders, so I need to do this programmatically rather than hard coding each.
For example:
test_user
assumes role safe_role
to access the s3 folder my-bucket/safe_role
I assumed that this would be simple with policy variables, but I've been stuck for days just trying to get this set up.
I can successfully create safe_role
, attach the policy to safe_role
, and assume the role via test_user
. With the following policy, I am successfully able to download objects from any folder since it gives complete access to the S3 bucket.
Here is the initial policy that works:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket/*",
"arn:aws:s3:::my-bucket"
]
}
]
}
Now to restrict access to the safe_role
folder I tried (along with a million other things) this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket/${aws:username}/*",
"arn:aws:s3:::my-bucket"
]
}
]
}
After assuming the safe_role
role, I am unable to download any objects from any folder, including my-bucket/safe_role
. I get the following error:
Exception has occurred: ClientError
An error occurred (403) when calling the HeadObject operation: Forbidden
Questions:
- In this case, should we expect the variable
${aws:username}
to becometest_user
orsafe_role
? - Is there some way to see the policy variable substitution in the console (or any other method) when making test requests?
- If this isn't the right variable, what policy variable will give me the role name?
Based on @jarmod comment and AWS docs, it looks like we can't get variables for roles in the way I thought. Is there some other way to get the same effect? Tags perhaps?
CodePudding user response:
According to IAM policy elements: Variables and tags - AWS Identity and Access Management, when an IAM Role is assumed:
aws:userid
=role-id:caller-specified-role-name
where role-id
is the unique id of the role and the caller-specified-role-name is specified by the RoleSessionName parameter passed to the AssumeRole request.
Therefore, it isn't as simple as just having the folder named the same as the role. It would need to be named after the role-id
and the RoleSessionName
.
CodePudding user response:
As jarmod points out, we can't use aws:username
because it isn't populated for an assumed role. But John correctly notes that we CAN use the aws:userid
field for an assumed role. (See IAM policy elements: Variables and tags - AWS Identity and Access Management)
For an assumed role, the userid takes the following form:
aws:userid = role-id:caller-specified-role-name
With this knowledge, we can now tag each object in the folder with user = role_name
. Once the objects are tagged, we can limit access by attaching the following policy to the role:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictAccessToFolderRoleName",
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket/*",
"arn:aws:s3:::my-bucket"
],
"Condition": {
"StringLike": {
"aws:userid": "*:${s3:ExistingObjectTag/user}"
}
}
}
]
}
This condition compares the second part of the userid caller-specified-role-name
with the user
tag on the object. If the two don't match, then the assumed role will not be able to download the object.
It should be noted that this role name is "caller specified". This is OK for my application, but may be a security concern in other cases.
If anyone else has a more secure solution, I would be willing to change the accepted answer!